diff --git a/backend-schema/README.md b/backend-schema/README.md index 009b04974..f11c42c92 100644 --- a/backend-schema/README.md +++ b/backend-schema/README.md @@ -2,24 +2,26 @@ ## Files in this directory -| File | Purpose | -|------|---------| -| `V1__client_schema_extensions.sql` | DB migration — run on Fineract MySQL/MariaDB | -| `ClientEntityKyc.java` | JPA Entity for `m_client_entity_kyc` table | -| `ClientAddressIndia.java` | JPA Entity for `m_client_address_india` table | -| `ClientEntityKycRepository.java` | Spring Data repository | -| `ClientAddressIndiaRepository.java` | Spring Data repository | -| `ClientEntityKycApiResource.java` | REST controller — `/clients/{id}/entity-kyc` | +| File | Purpose | +| ------------------------------------ | ----------------------------------------------- | +| `V1__client_schema_extensions.sql` | DB migration — run on Fineract MySQL/MariaDB | +| `ClientEntityKyc.java` | JPA Entity for `m_client_entity_kyc` table | +| `ClientAddressIndia.java` | JPA Entity for `m_client_address_india` table | +| `ClientEntityKycRepository.java` | Spring Data repository | +| `ClientAddressIndiaRepository.java` | Spring Data repository | +| `ClientEntityKycApiResource.java` | REST controller — `/clients/{id}/entity-kyc` | | `ClientAddressIndiaApiResource.java` | REST controller — `/clients/{id}/india-address` | ## How to apply ### Step 1: Run SQL Migration + ```bash mysql -u root -p fineract_tenantdb < V1__client_schema_extensions.sql ``` ### Step 2: Copy Java files to Fineract source + ``` ClientEntityKyc.java → fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ ClientAddressIndia.java → fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ @@ -30,7 +32,9 @@ ClientAddressIndiaApiResource.java → fineract-provider/src/main/java/org/apach ``` ### Step 3: Update existing Fineract Client API + In `ClientApiResource.java`, the `POST /clients` endpoint needs to accept these additional fields in the request body: + - `nationality`, `annualTurnover`, `creditLimit` - `isMinor`, `ageYears`, `ageMonths`, `ageDays` - `guardianName`, `guardianRelation`, `guardianMobile`, `guardianIdNumber`, `guardianAddress` @@ -38,9 +42,11 @@ In `ClientApiResource.java`, the `POST /clients` endpoint needs to accept these In `ClientWritePlatformServiceJpaRepositoryImpl.java`, map these fields to the `m_client` entity during create/update. ### Step 4: Register new API resources + Add to `fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractWebMvcConfigurer.java` or equivalent Spring config. ### Step 5: Rebuild Fineract + ```bash ./gradlew bootRun ``` @@ -48,28 +54,30 @@ Add to `fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/ ## API Reference ### Entity KYC -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/clients/{clientId}/entity-kyc` | Create/update entity KYC + MSME | -| GET | `/clients/{clientId}/entity-kyc` | Retrieve entity KYC | -| DELETE | `/clients/{clientId}/entity-kyc` | Delete entity KYC | + +| Method | Endpoint | Description | +| ------ | -------------------------------- | ------------------------------- | +| POST | `/clients/{clientId}/entity-kyc` | Create/update entity KYC + MSME | +| GET | `/clients/{clientId}/entity-kyc` | Retrieve entity KYC | +| DELETE | `/clients/{clientId}/entity-kyc` | Delete entity KYC | ### India Address -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/clients/{clientId}/india-address` | Save all 4 address types | -| GET | `/clients/{clientId}/india-address` | Get all addresses | -| GET | `/clients/{clientId}/india-address/{type}` | Get specific type (PERMANENT/CURRENT/OFFICE/CORRESPONDENCE) | -| PUT | `/clients/{clientId}/india-address/{type}` | Update specific type | -| DELETE | `/clients/{clientId}/india-address` | Delete all addresses | + +| Method | Endpoint | Description | +| ------ | ------------------------------------------ | ----------------------------------------------------------- | +| POST | `/clients/{clientId}/india-address` | Save all 4 address types | +| GET | `/clients/{clientId}/india-address` | Get all addresses | +| GET | `/clients/{clientId}/india-address/{type}` | Get specific type (PERMANENT/CURRENT/OFFICE/CORRESPONDENCE) | +| PUT | `/clients/{clientId}/india-address/{type}` | Update specific type | +| DELETE | `/clients/{clientId}/india-address` | Delete all addresses | ## Tables Summary -| Table | New/Modified | Records | -|-------|-------------|---------| -| `m_client` | Modified — 12 new columns | Guardian, nationality, minor, turnover | -| `m_client_non_person` | Modified — 13 new columns | MSME, CIN, enterprise PAN, signatory | -| `m_client_kyc` | Modified — 19 new columns | AML, freeze, form60/61, form15G/H, KYC doc | -| `m_client_entity_kyc` | **New** | Entity-specific KYC, MSME, signatory, auth doc | -| `m_client_address_india` | **New** | 4-type India address with PIN lookup fields | -| `m_code_value` | Modified | Added Gender: Transgender, Other | +| Table | New/Modified | Records | +| ------------------------ | ------------------------- | ---------------------------------------------- | +| `m_client` | Modified — 12 new columns | Guardian, nationality, minor, turnover | +| `m_client_non_person` | Modified — 13 new columns | MSME, CIN, enterprise PAN, signatory | +| `m_client_kyc` | Modified — 19 new columns | AML, freeze, form60/61, form15G/H, KYC doc | +| `m_client_entity_kyc` | **New** | Entity-specific KYC, MSME, signatory, auth doc | +| `m_client_address_india` | **New** | 4-type India address with PIN lookup fields | +| `m_code_value` | Modified | Added Gender: Transgender, Other | diff --git a/package-lock.json b/package-lock.json index 52ed90e45..db166ada6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "lightgallery": "^2.9.0", "lodash": "4.17.23", "moment": "^2.29.4", + "ngx-image-cropper": "^9.1.6", "ngx-mat-select-search": "^8.0.2", "rxjs": "^7.8.2", "signature_pad": "^5.1.3", @@ -18822,6 +18823,19 @@ "node": ">= 0.4.0" } }, + "node_modules/ngx-image-cropper": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-9.1.6.tgz", + "integrity": "sha512-b250YJ+jZovfqIj8vdEOrpEFay34be5f1Hpvg6Db68VMlvdyyuzboJdR0gCupbXtVcG6qQ86L7YG+SYxXJwApw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=17.3.0", + "@angular/core": ">=17.3.0" + } + }, "node_modules/ngx-mat-select-search": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-8.0.4.tgz", diff --git a/package.json b/package.json index 11771d707..156be4d23 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "lightgallery": "^2.9.0", "lodash": "4.17.23", "moment": "^2.29.4", + "ngx-image-cropper": "^9.1.6", "ngx-mat-select-search": "^8.0.2", "rxjs": "^7.8.2", "signature_pad": "^5.1.3", diff --git a/postman/CBS_Client_Creation_API.postman_collection.json b/postman/CBS_Client_Creation_API.postman_collection.json index a2bcf7480..2cab16946 100644 --- a/postman/CBS_Client_Creation_API.postman_collection.json +++ b/postman/CBS_Client_Creation_API.postman_collection.json @@ -145,7 +145,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/kyc", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "kyc"] + "path": [ + "clients", + "{{clientId}}", + "kyc" + ] }, "description": "Saves KYC details including PAN, Aadhaar, AML compliance, document info, risk category, and Form 60/15 data." } @@ -163,7 +167,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/kyc", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "kyc"] + "path": [ + "clients", + "{{clientId}}", + "kyc" + ] }, "description": "Fetches existing KYC data for a client." } @@ -197,7 +205,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/images", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "images"] + "path": [ + "clients", + "{{clientId}}", + "images" + ] }, "description": "Uploads client profile photograph." } @@ -236,7 +248,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/documents", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "documents"] + "path": [ + "clients", + "{{clientId}}", + "documents" + ] }, "description": "Uploads client signature as a document." } @@ -262,7 +278,11 @@ "url": { "raw": "{{baseUrl}}/client/{{clientId}}/addresses?type=101", "host": ["{{baseUrl}}"], - "path": ["client", "{{clientId}}", "addresses"], + "path": [ + "client", + "{{clientId}}", + "addresses" + ], "query": [ { "key": "type", @@ -295,7 +315,11 @@ "url": { "raw": "{{baseUrl}}/client/{{clientId}}/addresses?type=102", "host": ["{{baseUrl}}"], - "path": ["client", "{{clientId}}", "addresses"], + "path": [ + "client", + "{{clientId}}", + "addresses" + ], "query": [ { "key": "type", @@ -320,7 +344,11 @@ "url": { "raw": "{{baseUrl}}/client/addresses/template", "host": ["{{baseUrl}}"], - "path": ["client", "addresses", "template"] + "path": [ + "client", + "addresses", + "template" + ] }, "description": "Fetches address template with `addressTypeIdOptions` and `stateProvinceIdOptions`. Use these IDs in address save requests." } @@ -346,7 +374,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/familymembers", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "familymembers"] + "path": [ + "clients", + "{{clientId}}", + "familymembers" + ] }, "description": "Adds a family member to the client. Call once per member." } @@ -364,7 +396,11 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/familymembers", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "familymembers"] + "path": [ + "clients", + "{{clientId}}", + "familymembers" + ] }, "description": "Fetches all family members for a client." } @@ -403,7 +439,13 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}/familymembers/{{familyMemberId}}/documents", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}", "familymembers", "{{familyMemberId}}", "documents"] + "path": [ + "clients", + "{{clientId}}", + "familymembers", + "{{familyMemberId}}", + "documents" + ] }, "description": "Uploads a marriage certificate document for a family member. Set `familyMemberId` from the Add Family Member response." } @@ -421,7 +463,10 @@ "url": { "raw": "{{baseUrl}}/clients/{{clientId}}", "host": ["{{baseUrl}}"], - "path": ["clients", "{{clientId}}"] + "path": [ + "clients", + "{{clientId}}" + ] }, "description": "Fetches full client details to verify saved data." } diff --git a/src/app/accounting/accounting.component.html b/src/app/accounting/accounting.component.html index bb83b8e09..eeb29f912 100644 --- a/src/app/accounting/accounting.component.html +++ b/src/app/accounting/accounting.component.html @@ -18,7 +18,12 @@
- +
{{ 'labels.heading.Frequent Postings' | translate }} @@ -27,11 +32,18 @@ - +
{{ 'labels.heading.Create Journal Entries' | translate }} - {{ 'labels.text.Manual journal entry transactions recorded in a journal' | translate }} + {{ + 'labels.text.Manual journal entry transactions recorded in a journal' | translate + }}
@@ -45,20 +57,34 @@ - +
{{ 'labels.heading.Accounts Linked to Financial Activities' | translate }} - {{ 'labels.text.List of Financial Activities and GL Account Mappings' | translate }} + {{ + 'labels.text.List of Financial Activities and GL Account Mappings' | translate + }}
- +
{{ 'labels.heading.Migrate Opening Balances (Office-wise)' | translate }} - {{ 'labels.text.Set or update office-level opening balances for GL accounts' | translate }} + {{ + 'labels.text.Set or update office-level opening balances for GL accounts' | translate + }}
@@ -76,7 +102,9 @@
{{ 'labels.heading.Closing Entries' | translate }} - {{ 'labels.text.Journal entries made at the end of accounting period' | translate }} + {{ + 'labels.text.Journal entries made at the end of accounting period' | translate + }}
@@ -114,9 +142,15 @@

{{ 'labels.heading.Chart of Accounts' | translate }}

{{ 'labels.text.The Chart of Accounts - General Ledger Setup' | translate }}

- - - + + +
@@ -124,9 +158,15 @@

{{ 'labels.heading.Accounts Linked to Financial Activities' | translate }}

{{ 'labels.text.Accounts linked to Financial Activities' | translate }}

- - - + + +
@@ -134,9 +174,15 @@

{{ 'labels.heading.Migrate Opening Balances (Office-wise)' | translate }}

{{ 'labels.text.GLAccount Balances Application' | translate }}

- - - + + +
@@ -144,9 +190,15 @@

{{ 'labels.heading.Closing Entries' | translate }}

{{ 'labels.text.Accounting to journal entries' | translate }}

- - - + + +
@@ -154,8 +206,14 @@

{{ 'labels.heading.Create Journal Entries' | translate }}

{{ 'labels.text.Journal Entries Transactions' | translate }}

- - - + + +
diff --git a/src/app/accounting/accounting.component.scss b/src/app/accounting/accounting.component.scss index 33dfdd24b..2e153a32e 100644 --- a/src/app/accounting/accounting.component.scss +++ b/src/app/accounting/accounting.component.scss @@ -46,10 +46,21 @@ $accent: #0ea5e9; span { border-radius: 2px; - &:nth-child(1) { background: $brand-dark; } - &:nth-child(2) { background: $accent; } - &:nth-child(3) { background: #10b981; } - &:nth-child(4) { background: #cbd5e1; } + &:nth-child(1) { + background: $brand-dark; + } + + &:nth-child(2) { + background: $accent; + } + + &:nth-child(3) { + background: #10b981; + } + + &:nth-child(4) { + background: #cbd5e1; + } } } @@ -59,7 +70,9 @@ $accent: #0ea5e9; grid-template-columns: repeat(2, 1fr); gap: 12px; - @media (max-width: 768px) { grid-template-columns: 1fr; } + @media (width <= 768px) { + grid-template-columns: 1fr; + } } .acc-card { @@ -68,14 +81,16 @@ $accent: #0ea5e9; gap: 14px; padding: 16px 18px; background: #fff; - border: 1px solid rgba(0, 0, 0, 0.06); + border: 1px solid rgb(0 0 0 / 6%); border-radius: 8px; text-decoration: none; cursor: pointer; - transition: box-shadow 0.15s, transform 0.15s; + transition: + box-shadow 0.15s, + transform 0.15s; &:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); + box-shadow: 0 4px 16px rgb(0 0 0 / 8%); transform: translateY(-1px); } @@ -89,12 +104,35 @@ $accent: #0ea5e9; font-size: 16px; flex-shrink: 0; - &.blue { background: #e0f2fe; color: #0284c7; } - &.green { background: #d1fae5; color: #059669; } - &.purple { background: #ede9fe; color: #7c3aed; } - &.amber { background: #fef3c7; color: #d97706; } - &.teal { background: #ccfbf1; color: #0d9488; } - &.red { background: #fee2e2; color: #dc2626; } + &.blue { + background: #e0f2fe; + color: #0284c7; + } + + &.green { + background: #d1fae5; + color: #059669; + } + + &.purple { + background: #ede9fe; + color: #7c3aed; + } + + &.amber { + background: #fef3c7; + color: #d97706; + } + + &.teal { + background: #ccfbf1; + color: #0d9488; + } + + &.red { + background: #fee2e2; + color: #dc2626; + } } &__info { @@ -125,10 +163,18 @@ $accent: #0ea5e9; :host-context(.dark-theme) { .acc-card { background: #1e1e2e; - border-color: rgba(255, 255, 255, 0.06); + border-color: rgb(255 255 255 / 6%); } - .acc-card__title { color: #e0e0e0; } - .acc-card__desc { color: #666; } - .acc-header__title { color: #e0e0e0; } + .acc-card__title { + color: #e0e0e0; + } + + .acc-card__desc { + color: #666; + } + + .acc-header__title { + color: #e0e0e0; + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8d5313aeb..368a2b07e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -51,6 +51,7 @@ import { ConfigurationWizardModule } from './configuration-wizard/configuration- import { RemittancesModule } from './remittances/remittances.module'; import { SupersetAnalyticsModule } from './superset-analytics/superset-analytics.module'; import { CkycModule } from './ckyc/ckyc.module'; +import { KycManagementModule } from './kyc-management/kyc-management.module'; import { RbiCircularsModule } from './rbi-circulars/rbi-circulars.module'; import { CfcfrmsModule } from './cfcfrms/cfcfrms.module'; import { BankCalendarModule } from './bank-calendar/bank-calendar.module'; @@ -141,6 +142,7 @@ export function HttpLoaderFactory(http: HttpClient) { RemittancesModule, SupersetAnalyticsModule, CkycModule, + KycManagementModule, RbiCircularsModule, CfcfrmsModule, BankCalendarModule, diff --git a/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.html b/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.html index 688645f25..e98d6bc35 100644 --- a/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.html +++ b/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.html @@ -41,7 +41,14 @@
From Date - + @if (holidayForm.controls.fromDate.hasError('required')) { @@ -51,7 +58,14 @@ To Date - + @if (holidayForm.controls.toDate.hasError('required')) { @@ -90,7 +104,14 @@
Repayments Rescheduled To - + diff --git a/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.ts b/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.ts index 1ebc9251d..e10d3d65d 100644 --- a/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.ts +++ b/src/app/bank-calendar/add-holiday-dialog/add-holiday-dialog.component.ts @@ -42,12 +42,27 @@ export class AddHolidayDialogComponent implements OnInit { ngOnInit() { this.holidayForm = this.fb.group({ - name: ['', Validators.required], + name: [ + '', + Validators.required + ], description: [''], - fromDate: ['', Validators.required], - toDate: ['', Validators.required], - reschedulingType: ['', Validators.required], - officeId: ['', Validators.required] + fromDate: [ + '', + Validators.required + ], + toDate: [ + '', + Validators.required + ], + reschedulingType: [ + '', + Validators.required + ], + officeId: [ + '', + Validators.required + ] }); this.loadData(); diff --git a/src/app/bank-calendar/bank-calendar.component.html b/src/app/bank-calendar/bank-calendar.component.html index d8768ec66..b7f263ac6 100644 --- a/src/app/bank-calendar/bank-calendar.component.html +++ b/src/app/bank-calendar/bank-calendar.component.html @@ -111,7 +111,16 @@ [class.cal-grid__day--sat2nd4th]="(day.is2ndSaturday || day.is4thSaturday) && day.isCurrentMonth" [class.cal-grid__day--holiday]="day.isHoliday && day.isCurrentMonth" [class.cal-grid__day--working]="day.isWorking && day.isCurrentMonth" - [matTooltip]="day.holidayName || (day.is2ndSaturday ? '2nd Saturday Off' : day.is4thSaturday ? '4th Saturday Off' : day.isSunday && day.isCurrentMonth ? 'Sunday' : '')" + [matTooltip]=" + day.holidayName || + (day.is2ndSaturday + ? '2nd Saturday Off' + : day.is4thSaturday + ? '4th Saturday Off' + : day.isSunday && day.isCurrentMonth + ? 'Sunday' + : '') + " > {{ day.isCurrentMonth ? day.date : '' }}
diff --git a/src/app/bank-calendar/bank-calendar.component.scss b/src/app/bank-calendar/bank-calendar.component.scss index bc78d844e..a5ba73b92 100644 --- a/src/app/bank-calendar/bank-calendar.component.scss +++ b/src/app/bank-calendar/bank-calendar.component.scss @@ -6,6 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +@use 'sass:color'; + $navy: #0d1b2a; $blue: #1e88e5; $green: #10b981; @@ -239,7 +241,7 @@ $purple: #7c3aed; flex-shrink: 0; &:hover { - background: darken($blue, 8%); + background: color.adjust($blue, $lightness: -8%); box-shadow: 0 2px 8px rgb(30 136 229 / 35%); } } @@ -434,7 +436,7 @@ $purple: #7c3aed; } // ─── Responsive ─── -@media screen and (max-width: 1200px) { +@media screen and (width <= 1200px) { .cal-months { grid-template-columns: repeat(2, 1fr); } @@ -444,7 +446,7 @@ $purple: #7c3aed; } } -@media screen and (max-width: 768px) { +@media screen and (width <= 768px) { .cal-months { grid-template-columns: 1fr; } diff --git a/src/app/bank-calendar/bank-calendar.component.ts b/src/app/bank-calendar/bank-calendar.component.ts index 92c7d09c8..754f956ab 100644 --- a/src/app/bank-calendar/bank-calendar.component.ts +++ b/src/app/bank-calendar/bank-calendar.component.ts @@ -56,7 +56,15 @@ export class BankCalendarComponent implements OnInit { currentYear: number; months: MonthData[] = []; - weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + weekDays = [ + 'Sun', + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat' + ]; workingDaysConfig: string[] = []; holidays: any[] = []; overallStats = { working: 0, nonWorking: 0, holidays: 0, sundays: 0, sat2nd4th: 0, totalDays: 0 }; @@ -135,7 +143,14 @@ export class BankCalendarComponent implements OnInit { private loadWorkingDays() { // Default Indian banking working days: Mon-Sat (Sun off, 2nd & 4th Sat off) - this.workingDaysConfig = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA']; + this.workingDaysConfig = [ + 'MO', + 'TU', + 'WE', + 'TH', + 'FR', + 'SA' + ]; this.mergeGazettedHolidays(); this.buildCalendar(); @@ -157,7 +172,11 @@ export class BankCalendarComponent implements OnInit { for (const gh of gazetted) { const exists = this.holidays.some((h: any) => { if (h.fromDate) { - const [y, m, d] = h.fromDate; + const [ + y, + m, + d + ] = h.fromDate; return y === this.currentYear && m === gh.month && d === gh.day; } return false; @@ -165,8 +184,16 @@ export class BankCalendarComponent implements OnInit { if (!exists) { this.holidays.push({ name: gh.name, - fromDate: [this.currentYear, gh.month, gh.day], - toDate: [this.currentYear, gh.month, gh.day], + fromDate: [ + this.currentYear, + gh.month, + gh.day + ], + toDate: [ + this.currentYear, + gh.month, + gh.day + ], description: gh.name, status: { value: 'Active' } }); @@ -197,8 +224,18 @@ export class BankCalendarComponent implements OnInit { const today = new Date(); const monthNames = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' ]; const days: CalendarDay[] = []; @@ -287,7 +324,7 @@ export class BankCalendarComponent implements OnInit { isSunday: false, is2ndSaturday: false, is4thSaturday: false, - dayOfWeek: (days.length) % 7 + dayOfWeek: days.length % 7 }); } @@ -307,7 +344,11 @@ export class BankCalendarComponent implements OnInit { private getHolidayForDate(year: number, month: number, day: number): any { return this.holidays.find((h: any) => { if (h.fromDate) { - const [y, m, d] = h.fromDate; + const [ + y, + m, + d + ] = h.fromDate; return y === year && m === month + 1 && d === day; } return false; @@ -337,10 +378,28 @@ export class BankCalendarComponent implements OnInit { printHolidayList() { const monthNames = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]; + const dayNames = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday' ]; - const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // --- Section 1: Gazetted / API Holidays --- const gazettedList: { sno: number; date: string; day: string; occasion: string }[] = []; @@ -352,7 +411,11 @@ export class BankCalendarComponent implements OnInit { sortedHolidays.forEach((h: any, i: number) => { if (h.fromDate) { - const [y, m, d] = h.fromDate; + const [ + y, + m, + d + ] = h.fromDate; const date = new Date(y, m - 1, d); gazettedList.push({ sno: i + 1, @@ -406,7 +469,7 @@ export class BankCalendarComponent implements OnInit { } const buildRows = (list: typeof gazettedList) => - list.map(h => `${h.sno}${h.date}${h.day}${h.occasion}`).join(''); + list.map((h) => `${h.sno}${h.date}${h.day}${h.occasion}`).join(''); const html = ` diff --git a/src/app/centers/centers.component.html b/src/app/centers/centers.component.html index c41c52ce4..e12dbbb4f 100644 --- a/src/app/centers/centers.component.html +++ b/src/app/centers/centers.component.html @@ -25,16 +25,32 @@
- + {{ 'labels.inputs.Show Closed Centers' | translate }}
- - @@ -75,7 +91,12 @@ - + -
- - -
- -

{{ 'labels.heading.CKYC' | translate }}

-

{{ 'labels.text.This section is under development.' | translate }}

+
+ +
+
+
+
+

CKYC Verification

+

Batch · Upload · Match — CERSAI compliance pipeline

- - +
+ +
+ + +
+
+
+
+
Total batches
+
{{ totalBatches }}
+
+
+
+
+
+
Total records
+
{{ totalRecords }}
+
+
+
+
+
+
Pending upload
+
{{ totalPending }}
+
+
+
+
+
+
Uploaded
+
{{ totalUploaded }}
+
+
+
+
+
+
Total size
+
{{ formatBytes(totalSize) }}
+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ + +
+ + + @if (activeSection === 'zip') { +
+
+
+ +
+

ZIP Batch Details

+

CERSAI ZIP archives grouped by creation date

+
+
+ Phase 2 +
+ + @if (loading) { +
+ + Loading batches… +
+ } + + @if (error && !loading) { +
+ + {{ error }} +
+ } + + @if (!loading && !error && groups.length === 0) { +
+ +

No batches found

+

Create a batch from the Record Master to see it here.

+
+ } + + @if (!loading && groups.length > 0) { +
+ @for (group of groups; track group.dateLabel) { +
+ + + @if (group.expanded) { +
+ + + + + + + + + + + + + @for (batch of group.batches; track trackBatch($index, batch)) { + + + + + + + + + } + +
Batch IDFilenameRecordsSizeStatusActions
+ + {{ batch.batchId }} + + + {{ batch.filename }} + {{ batch.recordCount }}{{ formatBytes(batch.sizeBytes) }} + @if (batch.uploadedToCersai) { + + Uploaded + + } @else { + + Pending + + } + + + @if (batch.documentId) { + + } +
+
+ } +
+ } +
+ } +
+ } + + + @if (activeSection === 'sftp') { +
+
+
+ +
+

SFTP Upload & ACK · API Match

+

CERSAI SFTP transfer history with ACK receipts and per-batch logs

+
+
+ Phase 3-4 +
+ +
+
+
+
+
{{ sftpSuccessCount }}
+
Uploaded successfully
+
+
+
+
+
+
{{ sftpFailedCount }}
+
Failed
+
+
+
+
+
+
{{ sftpPendingCount }}
+
Pending upload
+
+
+
+ + @if (loading) { +
+ + Loading logs… +
+ } + + @if (!loading && sftpLogs.length === 0) { +
+ +

No SFTP upload history yet

+

Upload a batch from the Record Master to see CERSAI transfer logs here.

+
+ } + + @if (!loading && sftpLogs.length > 0) { +
+ @for (log of sftpLogs; track log.id) { +
+
+ @if (isSftpSuccess(log)) { + + } @else if (isSftpFailed(log)) { + + } @else { + + } +
+
+
+ {{ log.batchId }} + @if (isSftpSuccess(log)) { + + Upload successful + + } @else if (isSftpFailed(log)) { + Upload failed + } @else { + Pending + } + + + {{ log.uploadedOnLabel || log.createdOnLabel || '—' }} + +
+ +
+
+ Filename + {{ log.filename }} +
+
+ Records + {{ log.recordCount }} +
+
+ Size + {{ formatBytes(log.sizeBytes) }} +
+ @if (log.cersaiReference) { +
+ CERSAI Ref + {{ log.cersaiReference }} +
+ } +
+ + @if (log.uploadRemarks) { +
+ + {{ log.uploadRemarks }} +
+ } +
+ +
+ + @if (log.documentId) { + + } +
+
+ } +
+ } +
+ } + + + @if (showManifest) { +
+
+
+
+ + Batch Manifest +
+ +
+ +
+ @if (manifestLoading) { +
+ + Loading manifest… +
+ } + + @if (manifestError) { +
+ + {{ manifestError }} +
+ } + + @if (manifestBatch && !manifestLoading) { +
+
+
+ Batch ID + {{ manifestBatch.batchId }} +
+
+ Created on + {{ manifestBatch.createdOnLabel || '—' }} +
+
+ Records + {{ manifestBatch.recordCount }} +
+
+ Size + {{ formatBytes(manifestBatch.sizeBytes) }} +
+
+ Status + @if (manifestBatch.uploadedToCersai) { + Uploaded + } @else { + Pending + } +
+
+ Filename + {{ manifestBatch.filename }} +
+
+ Checksum + {{ manifestBatch.checksum }} +
+
+
+ + @if (manifestBatch.manifest?.length > 0) { +
+ + Packaged clients ({{ manifestBatch.manifest.length }}) +
+
+ + + + + + + + + + + + + @for (entry of manifestBatch.manifest; track entry.clientId) { + + + + + + + + + } + +
ClientPSIDAdTotal size
+
+ {{ entry.displayName || 'Client ' + entry.clientId }} + @if (entry.cifNumber) { + + {{ entry.cifNumber }} + + } +
+
+ + + + + + + + + + + + + + + + + {{ + formatBytes( + (entry.photo?.bytes || 0) + + (entry.signature?.bytes || 0) + + (entry.idProof?.bytes || 0) + + (entry.addressProof?.bytes || 0) + ) + }} +
+
+ } + } +
+ + +
+
+ }
diff --git a/src/app/ckyc/ckyc-verification/ckyc-verification.component.scss b/src/app/ckyc/ckyc-verification/ckyc-verification.component.scss new file mode 100644 index 000000000..f1c89c262 --- /dev/null +++ b/src/app/ckyc/ckyc-verification/ckyc-verification.component.scss @@ -0,0 +1,1341 @@ +/** + * 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/. + */ + +$primary: #1a73e8; +$primary-dark: #0c447c; +$primary-light: #e8f0fe; +$gray-50: #f8f9fa; +$gray-100: #f1f3f4; +$gray-200: #e8eaed; +$gray-500: #9aa0a6; +$gray-700: #5f6368; +$gray-900: #202124; +$white: #fff; +$green: #1d9e75; +$green-light: #e1f5ee; +$teal-900: #085041; +$teal-700: #0f6e56; +$red: #d85a30; +$red-light: #faece7; +$amber: #ba7517; +$amber-light: #faeeda; +$purple: #6a4cb3; +$purple-light: #efeafb; + +.cv-container { + padding: 22px 26px 40px; + max-width: 1400px; + margin: 0 auto; +} + +/* ---------- Header ---------- */ +.cv-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 18px; + flex-wrap: wrap; + gap: 12px; + + &__left { + display: flex; + align-items: center; + gap: 14px; + } + + &__icon { + width: 44px; + height: 44px; + border-radius: 10px; + background: linear-gradient(135deg, $teal-700, $green); + color: $white; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + box-shadow: 0 3px 10px rgba($green, 0.3); + flex-shrink: 0; + } + + &__title { + margin: 0; + font-size: 20px; + font-weight: 700; + color: $gray-900; + } + + &__sub { + margin: 2px 0 0; + font-size: 13px; + color: $gray-500; + } +} + +.cv-btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0 16px; + height: 38px; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + border: 1px solid $gray-200; + background: $white; + color: $gray-700; + cursor: pointer; + transition: all 0.15s; + + fa-icon { + font-size: 12px; + } + + &:hover:not(:disabled) { + background: $gray-50; + border-color: $gray-500; + } + + &--ghost { + /* default */ + } + + &--primary { + background: $primary; + border-color: $primary; + color: $white; + + &:hover:not(:disabled) { + background: #1557b5; + border-color: #1557b5; + } + } +} + +/* ---------- KPIs ---------- */ +.cv-kpis { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 14px; + margin-bottom: 18px; +} + +.cv-kpi { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + background: $white; + border: 1px solid $gray-200; + border-radius: 12px; + + &__icon { + width: 40px; + height: 40px; + border-radius: 10px; + color: $white; + display: flex; + align-items: center; + justify-content: center; + font-size: 15px; + flex-shrink: 0; + box-shadow: 0 2px 8px rgb(0 0 0 / 8%); + + &--blue { + background: linear-gradient(135deg, $primary, #4285f4); + } + + &--teal { + background: linear-gradient(135deg, $teal-700, $green); + } + + &--amber { + background: linear-gradient(135deg, $amber, #d4892f); + } + + &--green { + background: linear-gradient(135deg, $green, #2fb88c); + } + + &--purple { + background: linear-gradient(135deg, $purple, #8a6fd4); + } + } + + &__label { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.4px; + color: $gray-500; + margin-bottom: 2px; + } + + &__value { + font-size: 18px; + font-weight: 700; + color: $gray-900; + + &--mono { + font-family: 'SF Mono', Consolas, monospace; + font-size: 13px; + color: $primary; + } + } +} + +/* ---------- Filters ---------- */ +.cv-filters { + background: $white; + border: 1px solid $gray-200; + border-radius: 12px; + padding: 18px 20px; + margin-bottom: 18px; + + &__grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px 18px; + margin-bottom: 16px; + } + + &__actions { + display: flex; + justify-content: flex-end; + gap: 10px; + padding-top: 14px; + border-top: 1px dashed $gray-200; + } +} + +.cv-field { + display: flex; + flex-direction: column; + gap: 5px; + min-width: 0; +} + +.cv-label { + font-size: 11px; + font-weight: 600; + color: $gray-700; + display: inline-flex; + align-items: center; + gap: 6px; + + fa-icon { + color: $primary; + font-size: 11px; + } +} + +.cv-input { + width: 100%; + padding: 10px 12px; + border: 1px solid $gray-200; + border-radius: 8px; + background: $gray-50; + color: $gray-900; + font-size: 13px; + font-family: inherit; + height: 40px; + box-sizing: border-box; + transition: all 0.15s; + + &:focus { + outline: none; + border-color: $primary; + background: $white; + box-shadow: 0 0 0 3px rgba($primary, 0.1); + } +} + +select.cv-input { + appearance: none; + padding-right: 34px; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: right 12px center; + cursor: pointer; +} + +/* ---------- Section tabs ---------- */ +.cv-tabs { + display: flex !important; + gap: 10px !important; + margin-bottom: 16px !important; + background: $white !important; + border: 1px solid $gray-200 !important; + border-radius: 14px !important; + padding: 8px !important; + box-shadow: 0 1px 3px rgb(0 0 0 / 3%) !important; +} + +button.cv-tab, +.cv-tabs .cv-tab { + flex: 1 1 0 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + gap: 10px !important; + padding: 14px 20px !important; + min-height: 52px !important; + border: 1.5px solid transparent !important; + border-radius: 10px !important; + background: transparent !important; + color: $gray-700 !important; + font-size: 14px !important; + font-weight: 600 !important; + cursor: pointer !important; + transition: all 0.15s !important; + font-family: inherit !important; + white-space: nowrap !important; + min-width: 0 !important; + line-height: 1 !important; + outline: none !important; + box-shadow: none !important; + + > fa-icon { + font-size: 15px !important; + color: $gray-500 !important; + } + + > span:not(.cv-tab__badge, .cv-tab__count) { + display: inline-flex !important; + align-items: center !important; + } + + &:hover { + background: $gray-50 !important; + color: $gray-900 !important; + + > fa-icon { + color: $primary !important; + } + } + + &--active { + background: $primary-light !important; + border-color: rgba($primary, 0.35) !important; + color: $primary-dark !important; + box-shadow: 0 2px 8px rgba($primary, 0.12) !important; + + > fa-icon { + color: $primary !important; + } + } +} + +.cv-tab__badge { + display: inline-block !important; + padding: 3px 9px !important; + border-radius: 10px !important; + font-size: 9px !important; + font-weight: 700 !important; + text-transform: uppercase !important; + letter-spacing: 0.3px !important; + margin-left: 2px !important; + + &--blue { + background: $primary-light !important; + color: $primary-dark !important; + } + + &--amber { + background: $amber-light !important; + color: $amber !important; + } +} + +.cv-tab__count { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + min-width: 22px !important; + height: 20px !important; + padding: 0 8px !important; + border-radius: 10px !important; + background: $amber !important; + color: $white !important; + font-size: 10px !important; + font-weight: 700 !important; + font-variant-numeric: tabular-nums !important; +} + +@media (width <= 680px) { + .cv-tabs { + flex-direction: column !important; + } + + button.cv-tab, + .cv-tabs .cv-tab { + justify-content: space-between !important; + } +} + +/* Hide the per-section header badge since the tab strip already carries it */ +.cv-section .cv-badge { + display: none; +} + +/* ---------- Section card ---------- */ +.cv-section { + background: $white; + border: 1px solid $gray-200; + border-radius: 14px; + padding: 18px 20px 20px; + margin-bottom: 18px; + + &__head { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px solid $gray-100; + } + + &__title { + display: flex; + align-items: center; + gap: 12px; + + > fa-icon { + width: 36px; + height: 36px; + border-radius: 10px; + background: $primary-light; + color: $primary; + display: flex !important; + align-items: center; + justify-content: center; + font-size: 14px; + } + + h3 { + margin: 0; + font-size: 15px; + font-weight: 700; + color: $gray-900; + } + + p { + margin: 2px 0 0; + font-size: 11px; + color: $gray-500; + } + } + + &--placeholder { + background: linear-gradient(135deg, $gray-50, $primary-light); + } +} + +.cv-badge { + display: inline-block; + padding: 3px 10px; + border-radius: 10px; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.3px; + + &--blue { + background: $primary-light; + color: $primary-dark; + } + + &--amber { + background: $amber-light; + color: $amber; + } +} + +/* ---------- State banners ---------- */ +.cv-state { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 30px 20px; + border-radius: 10px; + font-size: 13px; + + fa-icon { + font-size: 18px; + } + + &--loading { + background: $primary-light; + color: $primary; + border: 1px dashed rgba($primary, 0.3); + } + + &--error { + background: $red-light; + color: $red; + border: 1px solid rgba($red, 0.25); + } + + &--empty { + background: $gray-50; + color: $gray-500; + border: 1px dashed $gray-200; + + fa-icon { + color: $primary; + margin-bottom: 4px; + } + + h4 { + margin: 0; + font-size: 15px; + font-weight: 600; + color: $gray-900; + } + + p { + margin: 0; + font-size: 12px; + } + } +} + +/* ---------- Batch groups ---------- */ +.cv-groups { + display: flex; + flex-direction: column; + gap: 10px; +} + +.cv-group { + background: $white; + border: 1px solid $gray-200; + border-radius: 10px; + overflow: hidden; + transition: + border-color 0.15s, + box-shadow 0.15s; + + &:hover { + border-color: rgba($primary, 0.3); + } + + &--open { + border-color: $primary-light; + box-shadow: 0 2px 10px rgba($primary, 0.05); + } + + &__head { + width: 100%; + padding: 14px 18px; + background: $gray-50; + border: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + cursor: pointer; + font-family: inherit; + text-align: left; + + &:hover { + background: $gray-100; + } + } + + &__left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; + min-width: 0; + } + + &__chev { + color: $primary; + font-size: 14px; + flex-shrink: 0; + } + + &__date-pill { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background: $primary-light; + color: $primary-dark; + border-radius: 10px; + font-size: 12px; + font-weight: 700; + + fa-icon { + color: $primary; + font-size: 11px; + } + } + + &__summary { + font-size: 12px; + color: $gray-700; + display: flex; + gap: 8px; + flex-wrap: wrap; + + strong { + color: $gray-900; + font-weight: 700; + } + } + + &__sep { + color: $gray-200; + } + + &__right { + display: flex; + gap: 6px; + flex-shrink: 0; + } + + &__body { + background: $white; + border-top: 1px solid $gray-200; + overflow-x: auto; + } +} + +.cv-pill { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + border-radius: 12px; + font-size: 11px; + font-weight: 700; + + fa-icon { + font-size: 10px; + } + + &--green { + background: $green-light; + color: $teal-900; + border: 1px solid rgba($green, 0.25); + } + + &--amber { + background: $amber-light; + color: $amber; + border: 1px solid rgba($amber, 0.3); + } + + &--red { + background: $red-light; + color: $red; + border: 1px solid rgba($red, 0.3); + } +} + +/* ---------- SFTP logs ---------- */ +.cv-sftp-kpis { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 12px; + padding: 16px 20px 4px; +} + +.cv-sftp-kpi { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + border-radius: 12px; + border: 1px solid $gray-200; + background: $white; + + &__icon { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; + } + + &__value { + font-size: 20px; + font-weight: 700; + color: $gray-900; + line-height: 1.1; + } + + &__label { + font-size: 11px; + color: $gray-500; + text-transform: uppercase; + letter-spacing: 0.4px; + margin-top: 2px; + } + + &--green { + border-color: rgba($green, 0.3); + background: linear-gradient(180deg, $green-light, $white); + + .cv-sftp-kpi__icon { + background: $green; + color: $white; + } + } + + &--red { + border-color: rgba($red, 0.3); + background: linear-gradient(180deg, $red-light, $white); + + .cv-sftp-kpi__icon { + background: $red; + color: $white; + } + } + + &--amber { + border-color: rgba($amber, 0.3); + background: linear-gradient(180deg, $amber-light, $white); + + .cv-sftp-kpi__icon { + background: $amber; + color: $white; + } + } +} + +.cv-sftp-logs { + display: flex; + flex-direction: column; + gap: 10px; + padding: 12px 20px 20px; +} + +.cv-sftp-log { + display: flex; + align-items: flex-start; + gap: 14px; + padding: 14px 16px; + border: 1px solid $gray-200; + border-left-width: 4px; + border-radius: 10px; + background: $white; + transition: + background 0.15s, + box-shadow 0.15s; + + &:hover { + background: #fafbfc; + box-shadow: 0 2px 8px rgb(0 0 0 / 4%); + } + + &--success { + border-left-color: $green; + } + + &--failed { + border-left-color: $red; + } + + &__icon { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + background: $gray-100; + color: $gray-500; + font-size: 16px; + flex-shrink: 0; + } + + &--success .cv-sftp-log__icon { + background: $green-light; + color: $green; + } + + &--failed .cv-sftp-log__icon { + background: $red-light; + color: $red; + } + + &__body { + flex: 1; + min-width: 0; + } + + &__head { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 8px; + } + + &__bid { + font-family: 'SF Mono', Consolas, monospace; + font-size: 13px; + font-weight: 700; + color: $primary; + + fa-icon { + font-size: 11px; + margin-right: 4px; + } + } + + &__time { + font-size: 11px; + color: $gray-500; + margin-left: auto; + + fa-icon { + font-size: 10px; + margin-right: 4px; + } + } + + &__meta { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 8px 16px; + font-size: 12px; + + > div { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + } + + &__label { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.4px; + color: $gray-500; + font-weight: 600; + } + + &__value { + color: $gray-900; + font-weight: 500; + word-break: break-all; + + &--mono { + font-family: 'SF Mono', Consolas, monospace; + font-size: 11px; + } + } + + &__remarks { + margin-top: 8px; + padding: 8px 10px; + border-radius: 8px; + background: $gray-50; + border: 1px solid $gray-200; + font-size: 12px; + color: $gray-700; + display: flex; + align-items: flex-start; + gap: 8px; + + fa-icon { + font-size: 12px; + color: $gray-500; + margin-top: 2px; + } + + &--err { + background: $red-light; + border-color: rgba($red, 0.3); + color: $red; + + fa-icon { + color: $red; + } + } + } + + &__actions { + display: flex; + align-items: center; + flex-shrink: 0; + } +} + +/* ---------- Batches table ---------- */ +.cv-batches { + width: 100%; + border-collapse: collapse; + font-size: 12px; + + thead tr { + background: $gray-50; + } + + th { + text-align: left; + padding: 10px 14px; + font-size: 10px; + font-weight: 700; + color: $gray-500; + text-transform: uppercase; + letter-spacing: 0.4px; + white-space: nowrap; + } + + td { + padding: 12px 14px; + border-top: 1px solid $gray-100; + color: $gray-900; + vertical-align: middle; + } + + tbody tr:hover { + background: #f5f8fc; + } + + &__num { + text-align: right; + white-space: nowrap; + font-variant-numeric: tabular-nums; + } + + &__actions { + text-align: right; + width: 100px; + } + + &__bid { + font-family: 'SF Mono', Consolas, monospace; + font-size: 12px; + font-weight: 600; + color: $primary; + white-space: nowrap; + + fa-icon { + color: $primary; + margin-right: 6px; + font-size: 11px; + } + } + + &__file { + color: $gray-700; + + fa-icon { + color: $primary; + margin-right: 6px; + font-size: 11px; + } + + span { + word-break: break-all; + max-width: 280px; + display: inline-block; + vertical-align: middle; + } + } +} + +.cv-action-btn { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid $gray-200; + background: $white; + color: $gray-700; + cursor: pointer; + margin-left: 4px; + transition: all 0.15s; + + fa-icon { + font-size: 12px; + } + + &:hover { + background: $primary-light; + color: $primary; + border-color: $primary; + } + + &--primary { + background: $primary; + color: $white; + border-color: $primary; + + &:hover { + background: #1557b5; + color: $white; + } + } +} + +/* ---------- SFTP placeholder ---------- */ +.cv-placeholder { + padding: 30px 24px; + text-align: center; + + &__icon { + width: 64px; + height: 64px; + border-radius: 16px; + background: $white; + border: 1px solid $primary-light; + color: $primary; + display: flex; + align-items: center; + justify-content: center; + font-size: 28px; + margin: 0 auto 12px; + box-shadow: 0 4px 14px rgba($primary, 0.1); + } + + h4 { + margin: 0 0 6px; + font-size: 15px; + font-weight: 700; + color: $gray-900; + } + + p { + margin: 0 auto 14px; + font-size: 12px; + color: $gray-700; + max-width: 480px; + line-height: 1.55; + } + + &__tags { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; + } +} + +.cv-chip { + padding: 5px 12px; + background: $white; + border: 1px solid $gray-200; + border-radius: 16px; + font-size: 11px; + font-weight: 600; + color: $gray-700; +} + +/* ---------- Manifest modal ---------- */ +.cv-modal { + position: fixed; + inset: 0; + background: rgb(15 25 45 / 55%); + backdrop-filter: blur(3px); + z-index: 100; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + + &__card { + background: $white; + border-radius: 16px; + width: 100%; + max-width: 720px; + max-height: 90vh; + display: flex; + flex-direction: column; + overflow: hidden; + box-shadow: 0 24px 60px rgb(0 0 0 / 25%); + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 18px 22px; + border-bottom: 1px solid $gray-100; + } + + &__title { + display: flex; + align-items: center; + gap: 10px; + font-size: 16px; + font-weight: 700; + color: $gray-900; + + fa-icon { + width: 34px; + height: 34px; + border-radius: 10px; + background: linear-gradient(135deg, $primary, #4285f4); + color: $white; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + box-shadow: 0 3px 10px rgba($primary, 0.3); + } + } + + &__close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid $gray-200; + background: $white; + color: $gray-500; + cursor: pointer; + + &:hover { + color: $red; + border-color: $red; + background: $red-light; + } + } + + &__body { + padding: 20px 22px; + overflow-y: auto; + flex: 1; + } + + &__footer { + padding: 16px 22px; + border-top: 1px solid $gray-100; + display: flex; + justify-content: flex-end; + gap: 10px; + background: $gray-50; + } +} + +.cv-manifest-head { + background: linear-gradient(135deg, $primary-light, $green-light); + border: 1px solid rgba($primary, 0.12); + border-radius: 10px; + padding: 16px 18px; + margin-bottom: 16px; + + &__grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px 20px; + } + + &__grid-span { + grid-column: 1 / -1; + } + + /* Force stacked layout inside each field */ + > .cv-manifest-head__grid > div { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; + + .cv-kpi__label { + display: block; + font-size: 9px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.5px; + color: $teal-700; + opacity: 0.85; + margin: 0; + } + + .cv-kpi__value { + display: block; + font-size: 13px; + font-weight: 700; + color: $gray-900; + word-break: break-word; + line-height: 1.35; + + &--mono { + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; + font-size: 11px; + color: $primary-dark; + letter-spacing: 0.2px; + word-break: break-all; + } + } + + .cv-pill { + align-self: flex-start; + } + } +} + +.cv-manifest-title { + font-size: 11px; + font-weight: 700; + color: $gray-500; + text-transform: uppercase; + letter-spacing: 0.5px; + margin: 10px 0 8px; + display: inline-flex; + align-items: center; + gap: 6px; + + fa-icon { + color: $primary; + font-size: 12px; + } +} + +.cv-manifest-table-wrap { + max-height: 320px; + overflow-y: auto; + border: 1px solid $gray-200; + border-radius: 10px; +} + +.cv-manifest-table { + width: 100%; + border-collapse: collapse; + font-size: 11px; + + thead tr { + background: $gray-50; + position: sticky; + top: 0; + z-index: 1; + } + + th { + text-align: left; + padding: 8px 12px; + font-size: 9px; + font-weight: 700; + color: $gray-500; + text-transform: uppercase; + letter-spacing: 0.4px; + white-space: nowrap; + border-bottom: 1px solid $gray-200; + } + + td { + padding: 8px 12px; + border-bottom: 1px solid $gray-100; + vertical-align: middle; + } + + tbody tr:last-child td { + border-bottom: none; + } + + tbody tr:hover { + background: #f5f8fc; + } +} + +.cv-th-c { + text-align: center !important; + width: 38px; +} + +.cv-th-r { + text-align: right !important; +} + +.cv-td-c { + text-align: center; +} + +.cv-td-r { + text-align: right; +} + +.cv-manifest-client { + display: flex; + flex-direction: column; + gap: 2px; + + strong { + font-size: 12px; + font-weight: 600; + color: $gray-900; + } +} + +.cv-manifest-cif { + font-size: 10px; + color: $gray-500; + font-family: 'SF Mono', Consolas, monospace; + + fa-icon { + font-size: 8px; + margin-right: 2px; + } +} + +.cv-dot { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 50%; + font-size: 9px; + font-weight: 700; + + &--ok { + background: $green-light; + color: $green; + border: 1px solid rgba($green, 0.3); + } + + &--miss { + background: $red-light; + color: $red; + border: 1px solid rgba($red, 0.3); + } +} + +.cv-manifest-size { + font-size: 11px; + font-weight: 600; + color: $gray-700; + font-family: 'SF Mono', Consolas, monospace; + white-space: nowrap; +} + +.cv-manifest-row--complete { + background: rgba($green, 0.03); +} + +/* ---------- Responsive ---------- */ +@media (width <= 1100px) { + .cv-kpis { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (width <= 700px) { + .cv-kpis { + grid-template-columns: repeat(2, 1fr); + } + + .cv-filters__grid { + grid-template-columns: 1fr; + } + + .cv-manifest-head__grid { + grid-template-columns: 1fr; + } +} diff --git a/src/app/ckyc/ckyc-verification/ckyc-verification.component.ts b/src/app/ckyc/ckyc-verification/ckyc-verification.component.ts index 8d62b9d5c..063114188 100644 --- a/src/app/ckyc/ckyc-verification/ckyc-verification.component.ts +++ b/src/app/ckyc/ckyc-verification/ckyc-verification.component.ts @@ -6,16 +6,312 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Component } from '@angular/core'; +import { Component, OnInit, DestroyRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ClientsService } from '../../clients/clients.service'; + +interface CkycBatch { + id: number; + batchId: string; + filename: string; + checksum: string; + recordCount: number; + sizeBytes: number; + documentId?: number; + createdOn: any; + uploadedToCersai: boolean; + uploadedOn?: any; + uploadStatus?: string; + uploadRemarks?: string; + cersaiReference?: string; + createdOnLabel?: string; + uploadedOnLabel?: string; +} + +interface CkycBatchGroup { + date: any; + dateLabel: string; + batchCount: number; + totalRecordCount: number; + totalSizeBytes: number; + uploadedCount: number; + pendingCount: number; + batches: CkycBatch[]; + expanded: boolean; +} @Component({ selector: 'mifosx-ckyc-verification', templateUrl: './ckyc-verification.component.html', + styleUrls: ['./ckyc-verification.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, - FaIconComponent + FaIconComponent, + FormsModule ] }) -export class CkycVerificationComponent {} +export class CkycVerificationComponent implements OnInit { + private clientsService = inject(ClientsService); + private destroyRef = inject(DestroyRef); + + filters: { fromDate: string; toDate: string; uploadedToCersai: '' | 'true' | 'false' } = { + fromDate: '', + toDate: '', + uploadedToCersai: '' + }; + + loading = false; + error = ''; + activeSection: 'zip' | 'sftp' = 'zip'; + groups: CkycBatchGroup[] = []; + totalBatches = 0; + totalRecords = 0; + totalSize = 0; + totalPending = 0; + totalUploaded = 0; + + /** SFTP log view */ + sftpLogs: CkycBatch[] = []; + sftpSuccessCount = 0; + sftpFailedCount = 0; + sftpPendingCount = 0; + + /** Manifest dialog */ + showManifest = false; + manifestLoading = false; + manifestError = ''; + manifestBatch: any = null; + + ngOnInit(): void { + this.fetch(); + } + + fetch(): void { + this.loading = true; + this.error = ''; + const query: any = { groupByDate: true }; + if (this.filters.fromDate) query.fromDate = this.filters.fromDate; + if (this.filters.toDate) query.toDate = this.filters.toDate; + if (this.filters.uploadedToCersai !== '') query.uploadedToCersai = this.filters.uploadedToCersai; + + this.clientsService + .getCkycBatches(query) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe({ + next: (res: any) => { + const rawGroups = res?.groups || []; + this.groups = rawGroups.map((g: any, idx: number) => ({ + date: g.date, + dateLabel: this.formatJavaDate(g.date), + batchCount: g.batchCount || 0, + totalRecordCount: g.totalRecordCount || 0, + totalSizeBytes: g.totalSizeBytes || 0, + uploadedCount: g.uploadedCount || 0, + pendingCount: g.pendingCount || 0, + batches: (g.batches || []).map((b: any) => ({ + ...b, + createdOnLabel: this.formatJavaDate(b.createdOn), + uploadedOnLabel: this.formatJavaDateTime(b.uploadedOn) + })), + expanded: idx === 0 + })); + this.computeSummary(); + this.computeSftpLogs(); + this.loading = false; + }, + error: (err: any) => { + this.loading = false; + this.error = err?.error?.defaultUserMessage || err?.message || 'Failed to load CKYC batches.'; + this.groups = []; + } + }); + } + + private computeSummary(): void { + this.totalBatches = 0; + this.totalRecords = 0; + this.totalSize = 0; + this.totalPending = 0; + this.totalUploaded = 0; + for (const g of this.groups) { + this.totalBatches += g.batchCount; + this.totalRecords += g.totalRecordCount; + this.totalSize += g.totalSizeBytes; + this.totalPending += g.pendingCount; + this.totalUploaded += g.uploadedCount; + } + } + + resetFilters(): void { + this.filters = { fromDate: '', toDate: '', uploadedToCersai: '' }; + this.fetch(); + } + + toggleGroup(group: CkycBatchGroup): void { + group.expanded = !group.expanded; + } + + openManifest(batch: CkycBatch, event: Event): void { + event.stopPropagation(); + this.showManifest = true; + this.manifestLoading = true; + this.manifestError = ''; + this.manifestBatch = { ...batch, createdOnLabel: this.formatJavaDate(batch.createdOn) }; + + this.clientsService + .getCkycBatchById(batch.id) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe({ + next: (res: any) => { + this.manifestBatch = { + ...res, + createdOnLabel: this.formatJavaDate(res.createdOn), + uploadedOnLabel: this.formatJavaDateTime(res.uploadedOn) + }; + this.manifestLoading = false; + }, + error: (err: any) => { + this.manifestLoading = false; + this.manifestError = err?.error?.defaultUserMessage || err?.message || 'Failed to load batch details.'; + } + }); + } + + closeManifest(): void { + this.showManifest = false; + this.manifestBatch = null; + this.manifestError = ''; + } + + downloadZip(batch: CkycBatch, event: Event): void { + event.stopPropagation(); + if (!batch.documentId) return; + this.clientsService + .downloadCkycBatchZip(batch.id, batch.documentId) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe({ + next: (blob: Blob) => { + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = batch.filename || `batch-${batch.batchId}.zip`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, + error: () => undefined + }); + } + + formatJavaDate(value: any): string { + if (!value) return ''; + if (Array.isArray(value) && value.length >= 3) { + const [ + y, + m, + d + ] = value; + return `${String(d).padStart(2, '0')}-${String(m).padStart(2, '0')}-${y}`; + } + if (typeof value === 'string') return value; + return ''; + } + + formatJavaDateTime(value: any): string { + if (value == null || value === '') return ''; + const pad = (n: any) => String(n ?? 0).padStart(2, '0'); + if (Array.isArray(value) && value.length >= 3) { + const [ + y, + mo, + d, + h = 0, + mi = 0, + s = 0 + ] = value; + const date = `${pad(d)}-${pad(mo)}-${y}`; + if (value.length >= 4) { + return `${date} ${pad(h)}:${pad(mi)}:${pad(s)}`; + } + return date; + } + if (typeof value === 'string') { + const dt = new Date(value); + if (!isNaN(dt.getTime())) { + return `${pad(dt.getDate())}-${pad(dt.getMonth() + 1)}-${dt.getFullYear()} ${pad(dt.getHours())}:${pad(dt.getMinutes())}:${pad(dt.getSeconds())}`; + } + return value; + } + return ''; + } + + private computeSftpLogs(): void { + const flat: CkycBatch[] = []; + for (const g of this.groups) { + for (const b of g.batches) { + if (b.uploadedToCersai || b.uploadStatus || b.uploadedOn) { + flat.push(b); + } + } + } + flat.sort((a, b) => { + const at = this.dateValue(a.uploadedOn); + const bt = this.dateValue(b.uploadedOn); + return bt - at; + }); + this.sftpLogs = flat; + this.sftpSuccessCount = flat.filter((b) => this.isSftpSuccess(b)).length; + this.sftpFailedCount = flat.filter((b) => this.isSftpFailed(b)).length; + this.sftpPendingCount = this.totalPending; + } + + isSftpSuccess(b: CkycBatch): boolean { + if (b.uploadStatus) return b.uploadStatus.toLowerCase() === 'success'; + return !!b.uploadedToCersai; + } + + isSftpFailed(b: CkycBatch): boolean { + return !!b.uploadStatus && b.uploadStatus.toLowerCase() === 'failed'; + } + + private dateValue(value: any): number { + if (Array.isArray(value) && value.length >= 3) { + const [ + y, + mo, + d, + h = 0, + mi = 0, + s = 0 + ] = value; + return new Date(y, (mo || 1) - 1, d || 1, h, mi, s).getTime(); + } + if (typeof value === 'string') { + const t = Date.parse(value); + return isNaN(t) ? 0 : t; + } + return 0; + } + + formatBytes(bytes: number | undefined): string { + if (!bytes) return '—'; + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / 1024 / 1024).toFixed(2)} MB`; + } + + trackBatch(_: number, b: CkycBatch): number { + return b.id; + } + + /** Completion check per manifest entry */ + isEntryComplete(entry: any): boolean { + return ( + entry?.photo?.packaged && entry?.signature?.packaged && entry?.idProof?.packaged && entry?.addressProof?.packaged + ); + } +} diff --git a/src/app/ckyc/ckyc.component.html b/src/app/ckyc/ckyc.component.html index 597ecb322..e1ce60539 100644 --- a/src/app/ckyc/ckyc.component.html +++ b/src/app/ckyc/ckyc.component.html @@ -6,120 +6,135 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- -
-
- - - - - - - - - +
+ +
+
+
+
- -
- - - - - - - - - +
+

CKYC Compliance Centre

+

+ Central KYC registry management — collect, upload and verify client artefacts against CERSAI +

- +
+ + CERSAI ready +
+
+ + +
+
+ 5-phase compliance pipeline + CBS → CKYC automation flow +
+ +
+
+
1
+
+
Mark Non-CKYC
+
Identify missing CKYC registrations
+
+
+ +
+
2
+
+
Auto ZIP
+
Bundle into CERSAI format
+
+
+ +
+
3 · 4
+
+
SFTP Upload & API Match
+
Transfer · ACK · Resolve CKYC numbers
+
+
+ +
+
5
+
+
Status Update
+
Update master & MIS
+
+
+
+ + +

Quick actions

+ +
diff --git a/src/app/ckyc/ckyc.component.scss b/src/app/ckyc/ckyc.component.scss index af9f4c075..d4c221397 100644 --- a/src/app/ckyc/ckyc.component.scss +++ b/src/app/ckyc/ckyc.component.scss @@ -5,3 +5,511 @@ * 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/. */ + +$primary: #1a73e8; +$primary-dark: #0c447c; +$primary-light: #e8f0fe; +$gray-50: #f8f9fa; +$gray-100: #f1f3f4; +$gray-200: #e8eaed; +$gray-500: #9aa0a6; +$gray-700: #5f6368; +$gray-900: #202124; +$white: #fff; +$teal: #0f6e56; +$teal-light: #e1f5ee; +$teal-bg: #f0fbf6; +$amber: #ba7517; +$amber-light: #faeeda; +$amber-bg: #fff8eb; +$purple: #6a4cb3; +$purple-light: #efeafb; +$purple-bg: #f8f5ff; + +.ckyc-dash { + padding: 24px 28px 40px; + max-width: 1400px; + margin: 0 auto; +} + +/* ---------- HERO ---------- */ +.ckyc-dash__hero { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 24px 28px; + background: linear-gradient(135deg, #0c447c 0%, #1a73e8 55%, #4285f4 100%); + border-radius: 16px; + color: $white; + margin-bottom: 22px; + position: relative; + overflow: hidden; + box-shadow: 0 6px 24px rgb(26 115 232 / 20%); + + &::before { + content: ''; + position: absolute; + top: -40%; + right: -10%; + width: 320px; + height: 320px; + border-radius: 50%; + background: radial-gradient(circle, rgb(255 255 255 / 15%), transparent 70%); + pointer-events: none; + } + + &-left { + display: flex; + align-items: center; + gap: 18px; + min-width: 0; + position: relative; + } + + &-icon { + width: 58px; + height: 58px; + border-radius: 14px; + background: rgb(255 255 255 / 18%); + border: 1px solid rgb(255 255 255 / 30%); + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + backdrop-filter: blur(10px); + flex-shrink: 0; + } + + &-badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: rgb(255 255 255 / 15%); + border: 1px solid rgb(255 255 255 / 30%); + border-radius: 30px; + font-size: 12px; + font-weight: 600; + backdrop-filter: blur(10px); + flex-shrink: 0; + position: relative; + + fa-icon { + color: #9fe1cb; + font-size: 14px; + } + } +} + +.ckyc-dash__title { + margin: 0; + font-size: 22px; + font-weight: 700; + letter-spacing: -0.3px; +} + +.ckyc-dash__sub { + margin: 4px 0 0; + font-size: 13px; + opacity: 0.85; + max-width: 560px; + line-height: 1.5; +} + +/* ---------- Pipeline banner ---------- */ +.ckyc-pipeline { + background: $white; + border: 1px solid $gray-200; + border-radius: 14px; + padding: 18px 20px; + margin-bottom: 22px; + + &__head { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px dashed $gray-200; + } + + &__heading { + font-size: 13px; + font-weight: 700; + color: $gray-900; + display: inline-flex; + align-items: center; + gap: 8px; + + fa-icon { + color: $primary; + } + } + + &__hint { + font-size: 11px; + color: $gray-500; + font-weight: 500; + } + + &__track { + display: flex; + align-items: stretch; + gap: 4px; + overflow-x: auto; + padding: 4px 2px; + + &::-webkit-scrollbar { + height: 4px; + } + + &::-webkit-scrollbar-thumb { + background: $gray-200; + border-radius: 4px; + } + } +} + +.ckyc-phase { + flex: 1; + min-width: 140px; + background: $primary-light; + border: 1px solid rgba($primary, 0.15); + border-radius: 10px; + padding: 12px 12px 14px; + text-align: center; + position: relative; + transition: transform 0.15s; + + &:hover { + transform: translateY(-2px); + } + + &__num { + position: absolute; + top: 8px; + right: 10px; + width: 20px; + height: 20px; + border-radius: 50%; + background: $primary; + color: $white; + font-size: 10px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + + &--dual { + width: auto; + min-width: 20px; + height: 20px; + padding: 0 8px; + border-radius: 10px; + font-size: 10px; + letter-spacing: 0.5px; + white-space: nowrap; + } + } + + &__icon { + width: 38px; + height: 38px; + border-radius: 10px; + background: $primary; + color: $white; + display: flex; + align-items: center; + justify-content: center; + font-size: 15px; + margin: 0 auto 8px; + box-shadow: 0 3px 10px rgba($primary, 0.25); + } + + &__title { + font-size: 12px; + font-weight: 700; + color: $primary-dark; + margin-bottom: 3px; + } + + &__desc { + font-size: 10px; + color: $gray-700; + line-height: 1.35; + } + + &__arrow { + display: flex; + align-items: center; + color: $gray-200; + font-size: 16px; + padding: 0 2px; + flex-shrink: 0; + } + + &--accent { + background: $amber-light; + border-color: rgba($amber, 0.25); + + .ckyc-phase__icon { + background: $amber; + box-shadow: 0 3px 10px rgba($amber, 0.3); + } + + .ckyc-phase__num { + background: $amber; + } + + .ckyc-phase__title { + color: #633806; + } + } + + &--wide { + flex: 1.8; + min-width: 210px; + } +} + +/* ---------- Section title ---------- */ +.ckyc-dash__section-title { + font-size: 14px; + font-weight: 700; + color: $gray-900; + margin: 10px 0 14px; + display: inline-flex; + align-items: center; + gap: 8px; + text-transform: uppercase; + letter-spacing: 0.4px; + + fa-icon { + color: $primary; + font-size: 14px; + } +} + +/* ---------- Menu grid ---------- */ +.ckyc-dash__grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 18px; +} + +.ckyc-card { + display: flex; + flex-direction: column; + padding: 22px 22px 18px; + background: $white; + border: 1px solid $gray-200; + border-radius: 16px; + text-decoration: none; + color: $gray-900; + transition: + transform 0.2s, + box-shadow 0.2s, + border-color 0.2s; + cursor: pointer; + position: relative; + overflow: hidden; + min-height: 220px; + + &__glow { + position: absolute; + top: -50px; + right: -50px; + width: 160px; + height: 160px; + border-radius: 50%; + opacity: 0.12; + pointer-events: none; + transition: + transform 0.4s, + opacity 0.3s; + } + + &:hover { + transform: translateY(-3px); + box-shadow: 0 10px 30px rgb(0 0 0 / 8%); + + .ckyc-card__glow { + transform: scale(1.25); + opacity: 0.2; + } + + .ckyc-card__cta { + background: $primary; + color: $white; + + fa-icon { + transform: translateX(3px); + } + } + } + + &__icon { + width: 52px; + height: 52px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + color: $white; + margin-bottom: 16px; + position: relative; + z-index: 1; + box-shadow: 0 6px 18px rgb(0 0 0 / 10%); + } + + &__body { + flex: 1; + position: relative; + z-index: 1; + } + + &__title { + font-size: 16px; + font-weight: 700; + color: $gray-900; + margin-bottom: 6px; + letter-spacing: -0.2px; + } + + &__desc { + font-size: 12px; + color: $gray-700; + line-height: 1.55; + margin: 0 0 12px; + } + + &__tags { + display: flex; + gap: 6px; + flex-wrap: wrap; + } + + &__cta { + display: inline-flex; + align-items: center; + gap: 8px; + align-self: flex-start; + padding: 8px 14px; + border-radius: 10px; + background: $gray-50; + color: $gray-700; + font-size: 12px; + font-weight: 600; + margin-top: 14px; + border: 1px solid $gray-200; + transition: all 0.2s; + position: relative; + z-index: 1; + + fa-icon { + font-size: 11px; + transition: transform 0.2s; + } + } + + /* ------ Colour variants ------ */ + &--blue { + .ckyc-card__icon { + background: linear-gradient(135deg, $primary, #4285f4); + } + + .ckyc-card__glow { + background: radial-gradient(circle, $primary, transparent 60%); + } + + &:hover .ckyc-card__cta { + background: $primary; + border-color: $primary; + } + } + + &--teal { + .ckyc-card__icon { + background: linear-gradient(135deg, $teal, #1d9e75); + } + + .ckyc-card__glow { + background: radial-gradient(circle, $teal, transparent 60%); + } + + &:hover .ckyc-card__cta { + background: $teal; + border-color: $teal; + } + } + + &--amber { + .ckyc-card__icon { + background: linear-gradient(135deg, $amber, #d4892f); + } + + .ckyc-card__glow { + background: radial-gradient(circle, $amber, transparent 60%); + } + + &:hover .ckyc-card__cta { + background: $amber; + border-color: $amber; + } + } + + &--purple { + .ckyc-card__icon { + background: linear-gradient(135deg, $purple, #8a6fd4); + } + + .ckyc-card__glow { + background: radial-gradient(circle, $purple, transparent 60%); + } + + &:hover .ckyc-card__cta { + background: $purple; + border-color: $purple; + } + } +} + +.ckyc-tag { + display: inline-block; + padding: 3px 9px; + background: $gray-100; + color: $gray-700; + font-size: 10px; + font-weight: 700; + border-radius: 10px; + text-transform: uppercase; + letter-spacing: 0.3px; +} + +/* ---------- Responsive ---------- */ +@media (width <= 900px) { + .ckyc-dash { + padding: 16px 18px 30px; + } + + .ckyc-dash__hero { + flex-direction: column; + align-items: flex-start; + } + + .ckyc-pipeline__track { + flex-flow: row nowrap; + } +} + +@media (width <= 600px) { + .ckyc-dash__grid { + grid-template-columns: 1fr; + } + + .ckyc-phase { + min-width: 120px; + } +} diff --git a/src/app/ckyc/ckyc.component.ts b/src/app/ckyc/ckyc.component.ts index 315569c26..521a90b25 100644 --- a/src/app/ckyc/ckyc.component.ts +++ b/src/app/ckyc/ckyc.component.ts @@ -7,11 +7,7 @@ */ import { Component } from '@angular/core'; -import { RouterLink } from '@angular/router'; -import { MatNavList, MatListItem } from '@angular/material/list'; -import { MatIcon } from '@angular/material/icon'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; -import { MatLine } from '@angular/material/grid-list'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @Component({ @@ -20,17 +16,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; styleUrls: ['./ckyc.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, - MatNavList, - MatListItem, - MatIcon, - FaIconComponent, - MatLine + FaIconComponent ] }) -export class CkycComponent { - arrowBooleans: boolean[] = new Array(4).fill(false); - - arrowBooleansToggle(arrowNumber: number) { - this.arrowBooleans[arrowNumber] = !this.arrowBooleans[arrowNumber]; - } -} +export class CkycComponent {} diff --git a/src/app/ckyc/record-master/record-master.component.html b/src/app/ckyc/record-master/record-master.component.html index c7ad688fc..e38c5bd8d 100644 --- a/src/app/ckyc/record-master/record-master.component.html +++ b/src/app/ckyc/record-master/record-master.component.html @@ -6,14 +6,978 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -
- - -
- -

{{ 'labels.heading.Record Master' | translate }}

-

{{ 'labels.text.This section is under development.' | translate }}

+
+ +
+
+
+
- - +
+

{{ 'labels.heading.Record Master' | translate }}

+

Clients missing CKYC registration or required artefacts

+
+
+
+ +
+
+ + +
+ + + +
+ + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + +
+
+ + + @if (loading) { +
+ + Loading CKYC records… +
+ } + + @if (error && !loading) { +
+ + {{ error }} +
+ } + + @if (!loading && !error && rows.length === 0) { +
+ +

No records found

+

All clients matching your filters have complete CKYC artefacts. 🎉

+
+ } + + + @if (!loading && rows.length > 0) { +
+
+ + + Showing {{ pageStart }}–{{ pageEnd }} of + {{ totalFilteredRecords }} + + @if (selected.size > 0) { + + + {{ selected.size }} selected + + } +
+ +
+ + + + + + + + + + + + + + @for (row of rows; track row.clientId) { + + + + + + + + + + } + +
+ + ClientContactAddressArtefactsCompletionActions
+ + +
+
{{ row.fullName?.charAt(0) || '?' }}
+
+
{{ row.fullName }}
+ +
+
+
+
+ + {{ row.email || 'No email' }} +
+
+ + {{ row.mobile || 'No mobile' }} +
+
+ + {{ row.address || 'No address on file' }} + +
+ + P + + + S + + + ID + + + Ad + +
+
+
+ {{ artefactCompletion(row) }}/4 +
+
+ + +
+
+ + +
+
+ Rows per page: + +
+
+ + {{ pageStart }}–{{ pageEnd }} + +
+
+
+ } + + + @if (selected.size > 0) { +
+
+
+ + {{ selected.size }} selected +
+ @if (selectedIncompleteCount > 0) { + + + {{ selectedIncompleteCount }} incomplete + + } +
+
+ + +
+
+ } + + + @if (showViewDialog && viewDetails) { +
+
+
+
+ + Client KYC details +
+ +
+ +
+ +
+
+ {{ viewDetails.row.fullName?.charAt(0) || '?' }} +
+
+
{{ viewDetails.row.fullName }}
+
+ + CIF {{ viewDetails.row.accountNo }} + + • + + + {{ viewDetails.row.email || 'No email' }} + + • + + + {{ viewDetails.row.mobile || 'No mobile' }} + +
+
+
+ + @if (viewLoading) { +
+ + Loading KYC details… +
+ } + + +
KYC Snapshot
+
+
+
KYC Status
+ @if (viewDetails.kycStatus) { +
+ {{ viewDetails.kycStatus }} +
+ } @else { +
—
+ } +
+ +
+
KYC Date
+
+ {{ viewDetails.kycDate || '—' }} +
+
+ +
+
Expiry Date
+
+ {{ viewDetails.expiryDate || '—' }} +
+
+ +
+
Risk Category
+
+ {{ viewDetails.riskCategory || '—' }} +
+
+
+ + +
CKYC Documents
+
+ + + + +
+ + +
+
+
+
+
{{ label }}
+
+ {{ slot.name }} + @if (slot.key) { + {{ slot.key }} + } +
+
+ +
+ + @if (slot.files?.length) { +
+ @for (file of slot.files; track file.id) { +
+ @if (file.previewUrl && file.isImage !== false && !file.isPdf) { + + } @else if (file.isPdf) { +
+ +
+ } @else { +
+ +
+ } +
+ @if (file.fileName) { + {{ file.fileName }} + } @else { + {{ file.name }} + } + +
+
+ } +
+ } @else if (slot.present) { +
+ + Loading preview… +
+ } +
+
+
+ + +
+
+ } + + + @if (showZipDialog) { +
+
+
+
+ + Auto ZIP Creation +
+ +
+ +
+

+ Bundling {{ selected.size }} client record{{ selected.size === 1 ? '' : 's' }} + into CERSAI-format ZIP. Files will be packaged with an SHA-256 checksum and registered for SFTP upload. +

+ + @if (selectedIncompleteCount > 0 && !zipRunning && !zipResult) { +
+ +
+ {{ selectedIncompleteCount }} record(s) are missing artefacts. + They will still be packaged but flagged as incomplete in the manifest. +
+
+ } + + @if (!zipRunning && !zipResult) { +
+ + +
+ } + + +
    + @for (phase of zipPhases; track phase.key; let i = $index) { +
  1. +
    + @if (phase.status === 'done') { + + } @else if (phase.status === 'active') { + + } @else if (phase.status === 'failed') { + + } @else { + {{ i + 1 }} + } +
    +
    +
    {{ phase.label }}
    + @if (phase.message) { +
    {{ phase.message }}
    + } +
    +
  2. + } +
+ + @if (zipResult && !zipResult.error) { +
+
+ + Batch created successfully +
+
+
+ Batch ID + {{ zipResult.batchId }} +
+
+ Filename + {{ zipResult.filename }} +
+
+ Records + {{ zipResult.recordCount }} +
+ @if (zipResult.sizeBytes) { +
+ Size + {{ (zipResult.sizeBytes / 1024 / 1024).toFixed(2) }} MB +
+ } + @if (zipResult.createdOn) { +
+ Created on + {{ zipResult.createdOn }} +
+ } + @if (zipResult.documentId) { +
+ Document ID + #{{ zipResult.documentId }} +
+ } +
+ Checksum + {{ zipResult.checksum }} +
+
+
+ + @if (zipResult.manifest && zipResult.manifest.length > 0) { +
+ + + @if (manifestExpanded) { +
+ + + + + + + + + + + + + @for (entry of zipResult.manifest; track entry.clientId) { + + + + + + + + + } + +
ClientPSIDAdTotal size
+
+ {{ entry.displayName || 'Client ' + entry.clientId }} + @if (entry.cifNumber) { + + {{ entry.cifNumber }} + + } +
+
+ + + + + + + + + + + + + + + + + {{ + formatBytes( + (entry.photo?.bytes || 0) + + (entry.signature?.bytes || 0) + + (entry.idProof?.bytes || 0) + + (entry.addressProof?.bytes || 0) + ) + }} +
+
+ } +
+ } + } + + @if (zipResult?.error) { +
+ +
+ Batch creation failed + {{ zipResult!.error }} +
+
+ } +
+ + +
+
+ } + + + @if (showUploadDialog) { +
+
+
+
+ + SFTP Upload to CERSAI +
+ +
+ +
+

+ Transferring batch to the CERSAI SFTP drop server and waiting for acknowledgement. On + success, the returned CKYC numbers will be saved against each client's master record. +

+ + @if (uploadResult?.status === 'ALREADY_UPLOADED') { +
+ +
+ Batch already uploaded. + {{ uploadResult?.remarks || 'No action was taken.' }} +
+
+ } + + +
    + @for (phase of uploadPhases; track phase.key; let i = $index) { +
  1. +
    + @if (phase.status === 'done') { + + } @else if (phase.status === 'active') { + + } @else if (phase.status === 'failed') { + + } @else { + {{ i + 1 }} + } +
    +
    +
    {{ phase.label }}
    + @if (phase.message) { +
    {{ phase.message }}
    + } +
    +
  2. + } +
+ + @if (uploadResult && !uploadResult.error) { +
+
+ + + @if (uploadResult.status === 'ALREADY_UPLOADED') { + Batch already uploaded to CERSAI + } @else { + Upload successful — ACK received + } + +
+
+
+ CERSAI Reference + {{ + uploadResult.cersaiReference + }} +
+
+ Batch ID + {{ uploadResult.batchId }} +
+
+ Uploaded on + {{ uploadResult.uploadedOn }} +
+ @if (uploadResult.receivedAt) { +
+ Received at + {{ uploadResult.receivedAt }} +
+ } +
+ Records + + {{ uploadResult.processedRecords }} / {{ uploadResult.totalRecords }} + +
+
+ Status + + + {{ uploadResult.status }} + + +
+
+ Checksum sent + {{ + uploadResult.checksumSent + }} +
+
+ Checksum verified + {{ + uploadResult.checksumVerified + }} +
+
+
+ + @if (uploadResult.perClientCkycNumbers && uploadResult.perClientCkycNumbers.length > 0) { +
+
+ + Issued CKYC numbers ({{ uploadResult.perClientCkycNumbers.length }}) +
+
+ + + + + + + + + + + @for (c of uploadResult.perClientCkycNumbers; track c.clientId) { + + + + + + + } + +
ClientCIFCKYC NumberStatus
#{{ c.clientId }}{{ c.cifNumber }}{{ c.ckycNumber }} + + {{ c.status }} + +
+
+
+ } + } + + @if (uploadResult?.error) { +
+ +
+ Upload failed + {{ uploadResult!.error }} +
+
+ } +
+ + +
+
+ }
diff --git a/src/app/ckyc/record-master/record-master.component.scss b/src/app/ckyc/record-master/record-master.component.scss new file mode 100644 index 000000000..7b63fd687 --- /dev/null +++ b/src/app/ckyc/record-master/record-master.component.scss @@ -0,0 +1,2157 @@ +/** + * 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/. + */ + +$primary: #1a73e8; +$primary-dark: #0c447c; +$primary-light: #e8f0fe; +$gray-50: #f8f9fa; +$gray-100: #f1f3f4; +$gray-200: #e8eaed; +$gray-500: #9aa0a6; +$gray-700: #5f6368; +$gray-900: #202124; +$white: #fff; +$green: #1d9e75; +$green-light: #e1f5ee; +$red: #d85a30; +$red-light: #faece7; +$amber: #ba7517; +$amber-light: #faeeda; +$teal-900: #085041; + +.rm-container { + padding: 20px 24px 40px; +} + +.rm-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 18px; + flex-wrap: wrap; + + &__left { + display: flex; + align-items: center; + gap: 14px; + min-width: 0; + } + + &__icon { + width: 44px; + height: 44px; + border-radius: 10px; + background: linear-gradient(135deg, $primary, #4285f4); + color: $white; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + box-shadow: 0 3px 10px rgb(26 115 232 / 25%); + flex-shrink: 0; + } + + &__title { + margin: 0; + font-size: 20px; + font-weight: 700; + color: $gray-900; + } + + &__sub { + margin: 2px 0 0; + font-size: 13px; + color: $gray-500; + } + + &__actions { + display: flex; + gap: 10px; + } +} + +.rm-btn { + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + height: 40px !important; + padding: 0 16px !important; + display: inline-flex !important; + align-items: center; + gap: 8px; + + fa-icon { + font-size: 13px; + } + + &--ghost { + color: $gray-700 !important; + border-color: $gray-200 !important; + } + + &--primary { + box-shadow: 0 1px 3px rgba($primary, 0.3) !important; + } +} + +/* 3-tab filter — elevated specificity + !important to beat Material button resets */ +.rm-tabs { + display: flex !important; + gap: 10px !important; + margin-bottom: 16px !important; + background: $white !important; + border: 1px solid $gray-200 !important; + border-radius: 14px !important; + padding: 8px !important; + box-shadow: 0 1px 3px rgb(0 0 0 / 3%) !important; +} + +button.rm-tab, +.rm-tabs .rm-tab { + flex: 1 1 0 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + gap: 10px !important; + padding: 12px 20px !important; + min-height: 48px !important; + height: auto !important; + border: 1.5px solid transparent !important; + border-radius: 10px !important; + background: transparent !important; + color: $gray-700 !important; + font-size: 13px !important; + font-weight: 600 !important; + cursor: pointer !important; + transition: all 0.15s !important; + font-family: inherit !important; + white-space: nowrap !important; + min-width: 0 !important; + line-height: 1 !important; + letter-spacing: 0.1px !important; + outline: none !important; + box-shadow: none !important; + + fa-icon { + font-size: 14px !important; + color: $gray-500 !important; + display: inline-flex !important; + align-items: center !important; + } + + > span { + display: inline-flex !important; + align-items: center !important; + } + + &:hover { + background: $gray-50 !important; + color: $gray-900 !important; + + fa-icon { + color: $primary !important; + } + } +} + +.rm-tab__count { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + min-width: 24px !important; + height: 22px !important; + padding: 0 9px !important; + border-radius: 11px !important; + background: $gray-100 !important; + color: $gray-700 !important; + font-size: 11px !important; + font-weight: 700 !important; + font-variant-numeric: tabular-nums !important; + margin-left: 2px !important; +} + +/* Active state — blue / amber / green variants */ +button.rm-tab.rm-tab--active, +.rm-tabs .rm-tab.rm-tab--active { + background: $primary-light !important; + border-color: rgba($primary, 0.35) !important; + color: $primary-dark !important; + box-shadow: 0 2px 8px rgba($primary, 0.12) !important; + + fa-icon { + color: $primary !important; + } + + .rm-tab__count { + background: $primary !important; + color: $white !important; + } +} + +button.rm-tab--amber.rm-tab--active, +.rm-tabs .rm-tab--amber.rm-tab--active { + background: $amber-light !important; + border-color: rgba($amber, 0.4) !important; + color: #633806 !important; + box-shadow: 0 2px 8px rgba($amber, 0.18) !important; + + fa-icon { + color: $amber !important; + } + + .rm-tab__count { + background: $amber !important; + color: $white !important; + } +} + +button.rm-tab--green.rm-tab--active, +.rm-tabs .rm-tab--green.rm-tab--active { + background: $green-light !important; + border-color: rgba($green, 0.4) !important; + color: $teal-900 !important; + box-shadow: 0 2px 8px rgba($green, 0.18) !important; + + fa-icon { + color: $green !important; + } + + .rm-tab__count { + background: $green !important; + color: $white !important; + } +} + +@media (width <= 680px) { + .rm-tabs { + flex-direction: column; + } + + .rm-tab { + justify-content: space-between; + } +} + +/* Filters */ +.rm-filters { + background: $white; + border: 1px solid $gray-200; + border-radius: 12px; + padding: 18px 20px; + margin-bottom: 18px; + + &__grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px 18px; + margin-bottom: 16px; + } + + &__actions { + display: flex; + justify-content: flex-end; + gap: 10px; + padding-top: 14px; + border-top: 1px dashed $gray-200; + } +} + +@media (width <= 1100px) { + .rm-filters__grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (width <= 720px) { + .rm-filters__grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (width <= 480px) { + .rm-filters__grid { + grid-template-columns: 1fr; + } +} + +.rm-field { + display: flex; + flex-direction: column; + gap: 5px; + min-width: 0; + + &--remarks { + margin: 4px 0 14px; + } +} + +.rm-textarea { + resize: vertical; + min-height: 50px; + height: auto; + font-family: inherit; + line-height: 1.5; +} + +.rm-label__opt { + font-weight: 500; + color: $gray-500; + font-size: 10px; + margin-left: 2px; +} + +.rm-label { + font-size: 11px; + font-weight: 600; + color: $gray-700; + display: inline-flex; + align-items: center; + gap: 6px; + + fa-icon { + color: $primary; + font-size: 11px; + } +} + +.rm-input { + width: 100%; + padding: 10px 12px; + border: 1px solid $gray-200; + border-radius: 8px; + background: $gray-50; + color: $gray-900; + font-size: 13px; + font-family: inherit; + height: 40px; + box-sizing: border-box; + transition: all 0.15s; + + &::placeholder { + color: $gray-500; + } + + &:focus { + outline: none; + border-color: $primary; + background: $white; + box-shadow: 0 0 0 3px rgb(26 115 232 / 10%); + } + + &--sm { + width: auto; + padding: 6px 10px; + font-size: 12px; + height: 30px; + } +} + +/* Native - Registration Date + Udyam Registration Number + + Registration Date - - Registration Status + + Registration Status Active Suspended Cancelled Pending Verification - - MSME Classification + + MSME Classification Micro (T/O ≤ ₹5 Cr, Inv ≤ ₹1 Cr) Small (T/O ≤ ₹50 Cr, Inv ≤ ₹10 Cr) Medium (T/O ≤ ₹250 Cr, Inv ≤ ₹50 Cr) - - Enterprise Type + + Enterprise Type Manufacturing Services Trading - - NIC Activity Code - + + NIC Activity Code +
Financial Thresholds (as per Udyam Portal)
- Annual Turnover — Last FY (₹ Cr) - - Investment — Plant & Machinery / Equipment (₹ Cr) - - Financial Year of Last Assessment + Annual Turnover — Last FY (₹ Cr) + + Investment — Plant & Machinery / Equipment (₹ Cr) + + Financial Year of Last Assessment 2025-26 2024-25 @@ -91,48 +129,72 @@ 2022-23 2021-22 2020-21 - +
Owner / Promoter Details
- Social Category of Owner + Social Category of Owner General Scheduled Caste (SC) Scheduled Tribe (ST) Other Backward Class (OBC) Minority - - Gender of Primary Owner + + Gender of Primary Owner Male Female Other - - No. of Employees (approx.) - + + No. of Employees (approx.) +
-
Women-led EnterpriseMajority ownership / management by women entrepreneur
+
+ Women-led EnterpriseMajority ownership / management by women entrepreneur +
-
Divyang (PWD) OwnedPersons with Disability — eligible for special scheme benefits
+
+ Divyang (PWD) OwnedPersons with Disability — eligible for special scheme benefits +
- MSMED Act 2006 (Revised): Classification based on composite criteria of investment in plant & machinery AND annual turnover. Udyam Registration is mandatory for availing PSL benefits, government procurement preference, and CGSS coverage. + MSMED Act 2006 (Revised): Classification based on composite criteria of investment in plant + & machinery AND annual turnover. Udyam Registration is mandatory for availing PSL benefits, government + procurement preference, and CGSS coverage.
-
@@ -143,14 +205,18 @@
- DPIIT + DPIIT
Startup India Registered
DPIIT recognition under Startup India initiative
- + expand_more
@@ -158,21 +224,36 @@
Registration Details
- DPIIT Recognition Number - - Recognition Date + DPIIT Recognition Number + + Recognition Date - - Certificate Expiry Date + + Certificate Expiry Date - - Entity Stage + + Entity Stage Early (0–2 years) Growth (2–5 years) Scale (5+ years) - - Sector as per DPIIT + + Sector as per DPIIT Fintech Agritech @@ -183,9 +264,12 @@ SaaS / B2B Tech Information Technology Other - - NIC Industry Code - + + NIC Industry Code +
Government Scheme Benefits Availed
@@ -200,25 +284,39 @@
PSL Linkage
- Loan Limit for PSL Eligibility (Cr) - - PSL Sub-category + Loan Limit for PSL Eligibility (Cr) + + PSL Sub-category Others (Startup India) MSME — Micro MSME — Small - +
- RBI Circular RBI/2023-24/62: DPIIT-recognized startups eligible for PSL classification up to ₹50 Cr. Recognition certificate to be retained in document management system. + RBI Circular RBI/2023-24/62: DPIIT-recognized startups eligible for PSL classification up to + ₹50 Cr. Recognition certificate to be retained in document management system.
-
@@ -229,14 +327,18 @@
- PMMY + PMMY
Mudra Borrower
Pradhan Mantri Mudra Yojana — SIDBI / Mudra scheme borrower
- + expand_more
@@ -244,22 +346,34 @@
Loan Classification
- Mudra Category + Mudra Category Shishu (up to ₹50,000) Kishore (₹50,001 – ₹5,00,000) Tarun (₹5,00,001 – ₹10,00,000) - - Mudra Loan Account No. - - Lending Institution Name - - Sanctioned Amount (₹) - - Disbursement Date + + Mudra Loan Account No. + + Lending Institution Name + + Sanctioned Amount (₹) + + Disbursement Date - - Loan Purpose + + Loan Purpose Manufacturing Trading @@ -267,37 +381,57 @@ Agriculture Allied Activity Transport Working Capital - +
Scheme Details
- MUDRA Card Number - - Current Outstanding (₹) - - Repayment Status + MUDRA Card Number + + Current Outstanding (₹) + + Repayment Status Regular Overdue (1–30 days) Overdue (31–90 days) NPA - +
-
NPA FlagMark if account classified as Non-Performing Asset
+
+ NPA FlagMark if account classified as Non-Performing Asset +
- PSL Tagging: Mudra loans must be tagged at both entity and account level for FIDD PSL returns. NPA flag mandatory for RBI quarterly reporting. + PSL Tagging: Mudra loans must be tagged at both entity and account level for FIDD PSL + returns. NPA flag mandatory for RBI quarterly reporting.
-
@@ -308,14 +442,18 @@
- EOU + EOU
Export Oriented Unit
Customs-registered EOU under DGFT / Ministry of Commerce
- + expand_more
@@ -323,9 +461,12 @@
EOU Registration
- EOU Registration Number - - Registered DC Zone + EOU Registration Number + + Registered DC Zone Mumbai (SEEPZ) Delhi (NSEZ) @@ -335,49 +476,89 @@ Hyderabad (VSEZ) Cochin (CSEZ) Other - - Bond / LUT Number - - Validity From + + Bond / LUT Number + + Validity From - - Validity To + + Validity To - - GSTIN under EOU Scheme - + + GSTIN under EOU Scheme +
Export Obligation & Compliance
- Annual Export Obligation (₹ Cr) - - NFE Target (₹ Cr) - - SoftEx Filing Status + Annual Export Obligation (₹ Cr) + + NFE Target (₹ Cr) + SoftEx Filing Status - CurrentPendingNot Applicable - - BRC Submission Status + CurrentPendingNot Applicable + + BRC Submission Status - CurrentPendingNot Applicable - - AD Category Bank Code - - FEMA Declaration Date + CurrentPendingNot Applicable + + AD Category Bank Code + + FEMA Declaration Date - +
- FEMA / RBI: EOU accounts must be tagged for FEMA compliance on forex receipts. AD Category-I bank declaration mandatory. EDPMS reporting required for all export proceeds. + FEMA / RBI: EOU accounts must be tagged for FEMA compliance on forex receipts. AD Category-I + bank declaration mandatory. EDPMS reporting required for all export proceeds.
-
@@ -388,14 +569,18 @@
- DGFT / IEC + DGFT / IEC
Import Export Business
Entity with valid DGFT Importer Exporter Code
- + expand_more
@@ -403,61 +588,106 @@
IEC Registration
- IEC Code (10-digit) - - IEC Issue Date + IEC Code (10-digit) + + IEC Issue Date - - IEC Status + + IEC Status ActiveSuspended - CancelledDeactivated - - Trade Type + CancelledDeactivated + + Trade Type Export OnlyImport Only Both Import & Export - - Primary HS Code (Commodity) - - Key Trading Countries - + + Primary HS Code (Commodity) + + Key Trading Countries +
Banking & Compliance
- AD Category Bank Code - - ECGC Policy Number - - ECGC Policy Expiry + AD Category Bank Code + + ECGC Policy Number + + ECGC Policy Expiry - +
-
EEFC Account RequiredExchange Earners Foreign Currency account
+
+ EEFC Account RequiredExchange Earners Foreign Currency account +
-
PCFC Facility RequiredPre-shipment Credit in Foreign Currency
+
+ PCFC Facility RequiredPre-shipment Credit in Foreign Currency +
-
FEMA Declaration ObtainedForm A2 / LRS declaration on file
+
+ FEMA Declaration ObtainedForm A2 / LRS declaration on file +
- RBI / FEMA: IEC mandatory before any import/export transaction. AD bank must report all export proceeds via EDPMS and import payments via IDPMS. ECGC cover recommended for high-risk countries. + RBI / FEMA: IEC mandatory before any import/export transaction. AD bank must report all + export proceeds via EDPMS and import payments via IDPMS. ECGC cover recommended for high-risk countries.
-
@@ -468,14 +698,18 @@
- RBI PSL + RBI PSL
Priority Sector Borrower
Eligible under RBI Priority Sector Lending guidelines
- + expand_more
@@ -483,7 +717,8 @@
PSL Classification
- PSL Category + PSL Category Agriculture MSME @@ -494,8 +729,10 @@ Export Credit Weaker Section Others - - PSL Sub-category + + PSL Sub-category Direct Agriculture — Crop Loan Direct Agriculture — Investment @@ -509,68 +746,115 @@ Weaker Section — Minorities Export Credit Others — Startup India - - PSL Loan Purpose Code - - PSL Eligibility Basis - - FIDD Reporting Code - - PSL Sanctioned Limit (₹) - + + PSL Loan Purpose Code + + PSL Eligibility Basis + + FIDD Reporting Code + + PSL Sanctioned Limit (₹) +
@if (pslForm.get('pslCategory')?.value === 'MICRO_ENTERPRISE') {
MSME Sub-classification
- MSME Classification + MSME Classification Micro (T/O ≤ ₹5 Cr, Inv ≤ ₹1 Cr) Small (T/O ≤ ₹50 Cr, Inv ≤ ₹10 Cr) Medium (T/O ≤ ₹250 Cr, Inv ≤ ₹50 Cr) - - Udyam Registration Number - - Udyam Registration Date + + Udyam Registration Number + + Udyam Registration Date - - Annual Turnover (₹ Cr) - - Investment — Plant & Machinery (₹ Cr) - + + Annual Turnover (₹ Cr) + + Investment — Plant & Machinery (₹ Cr) +
}
Special Category Flags
-
Weaker Section FlagSHG, JLG, SC/ST, OBC, Minorities, PWD borrowers
+
+ Weaker Section FlagSHG, JLG, SC/ST, OBC, Minorities, PWD borrowers +
-
SC / ST Borrower FlagScheduled Caste or Scheduled Tribe classification
+
+ SC / ST Borrower FlagScheduled Caste or Scheduled Tribe classification +
-
Women Entrepreneur FlagMajority-owned / managed by women
+
+ Women Entrepreneur FlagMajority-owned / managed by women +
-
Minority Community FlagAs defined by Ministry of Minority Affairs
+
+ Minority Community FlagAs defined by Ministry of Minority Affairs +
- RBI FIDD Circular: PSL tagging mandatory at entity AND loan account level. MSME classification must be verified from Udyam portal — self-declaration not acceptable. Weaker section loans reported separately in FIDD quarterly returns. + RBI FIDD Circular: PSL tagging mandatory at entity AND loan account level. MSME + classification must be verified from Udyam portal — self-declaration not acceptable. Weaker section loans + reported separately in FIDD quarterly returns.
-
@@ -581,14 +865,20 @@
- General + General
Non-Priority Sector
-
Standard corporate / retail borrower — no special priority sector classification
+
+ Standard corporate / retail borrower — no special priority sector classification +
- + expand_more
@@ -596,7 +886,8 @@
Sector Classification
- Industry Sector + Industry Sector Large Corporate Mid-Market Corporate @@ -607,62 +898,95 @@ Trust / NGO / Society Real Estate Other - - NIC Code (5-digit) - - Industry Description - - Annual Revenue Band + + NIC Code (5-digit) + + Industry Description + + Annual Revenue Band Below ₹1 Cr ₹1 Cr – ₹10 Cr ₹10 Cr – ₹100 Cr ₹100 Cr – ₹500 Cr Above ₹500 Cr - +
Risk Classification
- Risk Category + Risk Category Low Risk Medium Risk High Risk - - Customer Risk Score - - AML Risk Classification + + Customer Risk Score + + AML Risk Classification Standard Due Diligence (SDD) Customer Due Diligence (CDD) Enhanced Due Diligence (EDD) - +
-
PEP Flag — Politically Exposed PersonRequires mandatory EDD as per RBI KYC Master Circular
+
+ PEP Flag — Politically Exposed PersonRequires mandatory EDD as per RBI KYC Master Circular +
-
AML Watchlist MatchUN Sanctions / OFAC / EU / MHA match detected
+
+ AML Watchlist MatchUN Sanctions / OFAC / EU / MHA match detected +
-
Adverse Media FlagNegative news screening result — requires review
+
+ Adverse Media FlagNegative news screening result — requires review +
- RBI KYC Master Circular 2023: PEP flag triggers mandatory EDD regardless of transaction size. AML watchlist match must be escalated to MLRO within 24 hours. Risk score reviewed annually for High Risk customers. + RBI KYC Master Circular 2023: PEP flag triggers mandatory EDD regardless of transaction size. + AML watchlist match must be escalated to MLRO within 24 hours. Risk score reviewed annually for High Risk + customers.
-
diff --git a/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.scss b/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.scss index 083f40308..36e7a013d 100644 --- a/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.scss +++ b/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.scss @@ -1,18 +1,48 @@ +/** + * 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/. + */ + /** * Entity Classification — Scheme & Sector Classification */ -.ec-page { padding: 0; } +.ec-page { + padding: 0; +} .step-header { background: #0f172a; padding: 16px 24px; border-radius: 10px 10px 0 0; margin-bottom: 20px; - &__left { display: flex; align-items: center; gap: 12px; } - &__icon { font-size: 18px; color: #60a5fa; } - &__title { font-size: 16px; font-weight: 600; color: #fff; margin: 0; } - &__sub { font-size: 11px; color: rgba(255,255,255,0.5); margin: 2px 0 0; } + + &__left { + display: flex; + align-items: center; + gap: 12px; + } + + &__icon { + font-size: 18px; + color: #60a5fa; + } + + &__title { + font-size: 16px; + font-weight: 600; + color: #fff; + margin: 0; + } + + &__sub { + font-size: 11px; + color: rgb(255 255 255 / 50%); + margin: 2px 0 0; + } } .ec-chips-label { @@ -32,7 +62,11 @@ min-height: 28px; } -.ec-no-chip { font-size: 12px; color: #94a3b8; padding: 4px 0; } +.ec-no-chip { + font-size: 12px; + color: #94a3b8; + padding: 4px 0; +} .ec-chip { padding: 4px 10px; @@ -50,7 +84,10 @@ height: 14px !important; cursor: pointer; opacity: 0.6; - &:hover { opacity: 1; } + + &:hover { + opacity: 1; + } } .ec-card { @@ -60,7 +97,10 @@ margin-bottom: 12px; overflow: hidden; transition: border-color 0.2s; - &--open { border-color: #93c5fd; } + + &--open { + border-color: #93c5fd; + } } .ec-card-head { @@ -71,10 +111,17 @@ cursor: pointer; user-select: none; transition: background 0.15s; - &:hover { background: #f8fafc; } + + &:hover { + background: #f8fafc; + } } -.ec-card-head-left { display: flex; align-items: center; gap: 12px; } +.ec-card-head-left { + display: flex; + align-items: center; + gap: 12px; +} .ec-tag { padding: 3px 10px; @@ -85,14 +132,31 @@ white-space: nowrap; } -.ec-card-title { font-size: 14px; font-weight: 600; color: #1e293b; } -.ec-card-desc { font-size: 12px; color: #64748b; margin-top: 2px; } -.ec-card-toggle { display: flex; align-items: center; gap: 10px; } +.ec-card-title { + font-size: 14px; + font-weight: 600; + color: #1e293b; +} + +.ec-card-desc { + font-size: 12px; + color: #64748b; + margin-top: 2px; +} + +.ec-card-toggle { + display: flex; + align-items: center; + gap: 10px; +} .ec-chevron { transition: transform 0.25s; color: #94a3b8; - &.open { transform: rotate(180deg); } + + &.open { + transform: rotate(180deg); + } } .ec-card-body { @@ -105,7 +169,10 @@ grid-template-columns: repeat(3, 1fr); gap: 4px 16px; margin-bottom: 8px; - mat-form-field { width: 100%; } + + mat-form-field { + width: 100%; + } } .ec-grid-2 { @@ -113,7 +180,10 @@ grid-template-columns: repeat(2, 1fr); gap: 4px 16px; margin-bottom: 8px; - mat-form-field { width: 100%; } + + mat-form-field { + width: 100%; + } } .ec-sub-title { @@ -125,7 +195,12 @@ margin: 16px 0 12px; padding-top: 12px; border-top: 1px dashed #e2e8f0; - &:first-child { margin-top: 0; padding-top: 0; border-top: none; } + + &:first-child { + margin-top: 0; + padding-top: 0; + border-top: none; + } } .ec-check-grid { @@ -133,13 +208,20 @@ grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 8px; + mat-checkbox { padding: 8px 12px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 12px; - transition: background 0.12s, border-color 0.12s; - &:hover { background: #f8fafc; border-color: #93c5fd; } + transition: + background 0.12s, + border-color 0.12s; + + &:hover { + background: #f8fafc; + border-color: #93c5fd; + } } } @@ -160,8 +242,19 @@ border-radius: 6px; } -.ec-flag-label { font-size: 13px; font-weight: 500; color: #1e293b; display: block; } -.ec-flag-sub { font-size: 11px; color: #94a3b8; display: block; margin-top: 1px; } +.ec-flag-label { + font-size: 13px; + font-weight: 500; + color: #1e293b; + display: block; +} + +.ec-flag-sub { + font-size: 11px; + color: #94a3b8; + display: block; + margin-top: 1px; +} .ec-reg-note { margin-top: 16px; @@ -172,7 +265,10 @@ font-size: 12px; color: #0c4a6e; line-height: 1.6; - strong { font-weight: 600; } + + strong { + font-weight: 600; + } } .ec-card-actions { @@ -181,7 +277,12 @@ margin-top: 16px; padding-top: 12px; border-top: 1px solid #f1f5f9; - button { display: flex; align-items: center; gap: 6px; } + + button { + display: flex; + align-items: center; + gap: 6px; + } } .ec-info-banner { @@ -197,10 +298,27 @@ margin-top: 12px; } -@media (max-width: 900px) { .ec-grid-3 { grid-template-columns: repeat(2, 1fr); } } -@media (max-width: 600px) { - .ec-grid-3, .ec-grid-2 { grid-template-columns: 1fr; } - .ec-check-grid { grid-template-columns: 1fr; } - .ec-card-head { padding: 12px 14px; } - .ec-card-body { padding: 14px; } +@media (width <= 900px) { + .ec-grid-3 { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (width <= 600px) { + .ec-grid-3, + .ec-grid-2 { + grid-template-columns: 1fr; + } + + .ec-check-grid { + grid-template-columns: 1fr; + } + + .ec-card-head { + padding: 12px 14px; + } + + .ec-card-body { + padding: 14px; + } } diff --git a/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.ts b/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.ts index fb75cf2be..b3e6b9afc 100644 --- a/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.ts +++ b/src/app/clients/client-stepper/client-entity-kyc-step/client-entity-kyc-step.component.ts @@ -1,5 +1,9 @@ /** * 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, Input, OnInit, OnChanges, SimpleChanges, inject } from '@angular/core'; @@ -54,13 +58,69 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { /** Category metadata */ categories: CategoryMeta[] = [ - { id: 'msme', label: 'MSME', tag: 'Udyam / MSME', tagColor: '#0A5C6B', tagBg: '#E6F4F6', desc: 'Micro, Small & Medium Enterprise — Ministry of MSME / Udyam Registration', icon: 'cogs' }, - { id: 'startup', label: 'Startup India Registered', tag: 'DPIIT', tagColor: '#1A5FAB', tagBg: '#EAF2FB', desc: 'DPIIT recognition under Startup India initiative', icon: 'rocket' }, - { id: 'mudra', label: 'Mudra Borrower', tag: 'PMMY', tagColor: '#8A5A00', tagBg: '#FEF3DC', desc: 'Pradhan Mantri Mudra Yojana — SIDBI / Mudra scheme', icon: 'hand-holding-usd' }, - { id: 'eou', label: 'Export Oriented Unit', tag: 'EOU', tagColor: '#1B7A4A', tagBg: '#E8F5EE', desc: 'Customs-registered EOU under DGFT / Ministry of Commerce', icon: 'ship' }, - { id: 'importexport', label: 'Import Export Business', tag: 'DGFT / IEC', tagColor: '#5B3A8A', tagBg: '#F0EBF8', desc: 'Entity with valid DGFT Importer Exporter Code', icon: 'exchange-alt' }, - { id: 'psl', label: 'Priority Sector Borrower', tag: 'RBI PSL', tagColor: '#7B1F1F', tagBg: '#FCEAEA', desc: 'Eligible under RBI Priority Sector Lending guidelines', icon: 'bullseye' }, - { id: 'nps', label: 'Non-Priority Sector', tag: 'General', tagColor: '#444441', tagBg: '#F1EFE8', desc: 'Standard corporate — no special priority sector classification', icon: 'building' } + { + id: 'msme', + label: 'MSME', + tag: 'Udyam / MSME', + tagColor: '#0A5C6B', + tagBg: '#E6F4F6', + desc: 'Micro, Small & Medium Enterprise — Ministry of MSME / Udyam Registration', + icon: 'cogs' + }, + { + id: 'startup', + label: 'Startup India Registered', + tag: 'DPIIT', + tagColor: '#1A5FAB', + tagBg: '#EAF2FB', + desc: 'DPIIT recognition under Startup India initiative', + icon: 'rocket' + }, + { + id: 'mudra', + label: 'Mudra Borrower', + tag: 'PMMY', + tagColor: '#8A5A00', + tagBg: '#FEF3DC', + desc: 'Pradhan Mantri Mudra Yojana — SIDBI / Mudra scheme', + icon: 'hand-holding-usd' + }, + { + id: 'eou', + label: 'Export Oriented Unit', + tag: 'EOU', + tagColor: '#1B7A4A', + tagBg: '#E8F5EE', + desc: 'Customs-registered EOU under DGFT / Ministry of Commerce', + icon: 'ship' + }, + { + id: 'importexport', + label: 'Import Export Business', + tag: 'DGFT / IEC', + tagColor: '#5B3A8A', + tagBg: '#F0EBF8', + desc: 'Entity with valid DGFT Importer Exporter Code', + icon: 'exchange-alt' + }, + { + id: 'psl', + label: 'Priority Sector Borrower', + tag: 'RBI PSL', + tagColor: '#7B1F1F', + tagBg: '#FCEAEA', + desc: 'Eligible under RBI Priority Sector Lending guidelines', + icon: 'bullseye' + }, + { + id: 'nps', + label: 'Non-Priority Sector', + tag: 'General', + tagColor: '#444441', + tagBg: '#F1EFE8', + desc: 'Standard corporate — no special priority sector classification', + icon: 'building' + } ]; /** Toggle & expand state per category */ @@ -89,7 +149,15 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if (changes['readOnly']) { - const forms = [this.msmeForm, this.startupForm, this.mudraForm, this.eouForm, this.importexportForm, this.pslForm, this.npsForm]; + const forms = [ + this.msmeForm, + this.startupForm, + this.mudraForm, + this.eouForm, + this.importexportForm, + this.pslForm, + this.npsForm + ]; for (const form of forms) { if (!form) continue; if (this.readOnly) { @@ -213,14 +281,22 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { getForm(categoryId: string): UntypedFormGroup { switch (categoryId) { - case 'msme': return this.msmeForm; - case 'startup': return this.startupForm; - case 'mudra': return this.mudraForm; - case 'eou': return this.eouForm; - case 'importexport': return this.importexportForm; - case 'psl': return this.pslForm; - case 'nps': return this.npsForm; - default: return this.fb.group({}); + case 'msme': + return this.msmeForm; + case 'startup': + return this.startupForm; + case 'mudra': + return this.mudraForm; + case 'eou': + return this.eouForm; + case 'importexport': + return this.importexportForm; + case 'psl': + return this.pslForm; + case 'nps': + return this.npsForm; + default: + return this.fb.group({}); } } @@ -236,7 +312,7 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { } get enabledCategories(): CategoryMeta[] { - return this.categories.filter(c => this.enabled[c.id]); + return this.categories.filter((c) => this.enabled[c.id]); } get enabledCount(): number { @@ -266,7 +342,7 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { next: () => { this.saving[categoryId] = false; this.saved[categoryId] = true; - setTimeout(() => this.saved[categoryId] = false, 3000); + setTimeout(() => (this.saved[categoryId] = false), 3000); }, error: () => { this.saving[categoryId] = false; @@ -306,6 +382,10 @@ export class ClientEntityKycStepComponent implements OnInit, OnChanges { } // Keep old getter for backward compatibility - get entityKycData(): any { return this.classificationData; } - get hasEntityKycData(): boolean { return this.hasClassificationData; } + get entityKycData(): any { + return this.classificationData; + } + get hasEntityKycData(): boolean { + return this.hasClassificationData; + } } diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html index 9053104fc..f21112a45 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html @@ -1,3 +1,11 @@ + + @@ -10,7 +18,6 @@
- @if (isViewMode) { @@ -18,20 +25,31 @@
@if (linkedProfileImage) { - Profile + Profile } @else { person }
{{ selectedClient.displayName }} - Client ID: {{ selectedClient.id }}{{ selectedClient.accountNumber ? ' · A/C: ' + selectedClient.accountNumber : '' }} + Client ID: {{ selectedClient.id + }}{{ selectedClient.accountNumber ? ' · A/C: ' + selectedClient.accountNumber : '' }}
@if (linkedSignatureImage) {
- Signature + Signature Signature
} @@ -57,7 +75,11 @@
Date of Birth - {{ familyMemberForm.get('dateOfBirth')?.value ? (familyMemberForm.get('dateOfBirth')?.value | date:'dd MMM yyyy') : '—' }} + {{ + familyMemberForm.get('dateOfBirth')?.value + ? (familyMemberForm.get('dateOfBirth')?.value | date: 'dd MMM yyyy') + : '—' + }}
Nationality @@ -89,19 +111,25 @@ Status {{ familyMemberForm.get('isAlive')?.value !== false ? 'Alive' : 'Deceased' }} - @if (familyMemberForm.get('isDependent')?.value) { · Dependent } + @if (familyMemberForm.get('isDependent')?.value) { + · Dependent + }
@if (isSpouse && familyMemberForm.get('marriageDate')?.value) {
Marriage Date - {{ familyMemberForm.get('marriageDate')?.value | date:'dd MMM yyyy' }} + {{ + familyMemberForm.get('marriageDate')?.value | date: 'dd MMM yyyy' + }}
} @if (isSpouse && familyMemberForm.get('marriageAuthority')?.value) {
Issuing Authority - {{ getMarriageAuthorityLabel(familyMemberForm.get('marriageAuthority')?.value) }} + {{ + getMarriageAuthorityLabel(familyMemberForm.get('marriageAuthority')?.value) + }}
}
@@ -136,7 +164,9 @@ @if (searching) {
-
Searching...
+
+ Searching... +
} @if (!searching && searchResults.length > 0) { @@ -146,7 +176,18 @@ person
{{ r.displayName }} - ID: {{ r.id }} · {{ r.officeName }}@if (r.dateOfBirth) { · DOB: {{ r.dateOfBirth | dateFormat }} }@if (r.fatherSpouseName) { · {{ r.fatherSpouseName }} }@if (r.mobileNo) { · {{ r.mobileNo }} } + ID: {{ r.id }} · {{ r.officeName }} + @if (r.dateOfBirth) { + · DOB: {{ r.dateOfBirth | dateFormat }} + } + @if (r.fatherSpouseName) { + · {{ r.fatherSpouseName }} + } + @if (r.mobileNo) { + · {{ r.mobileNo }} + } +
} @@ -162,24 +203,37 @@
@if (linkedProfileImage) { - Profile + Profile } @else { person }
{{ selectedClient.displayName }} - Client ID: {{ selectedClient.id }}{{ selectedClient.accountNumber ? ' · A/C: ' + selectedClient.accountNumber : '' }} + Client ID: {{ selectedClient.id + }}{{ selectedClient.accountNumber ? ' · A/C: ' + selectedClient.accountNumber : '' }}
@if (linkedSignatureImage) {
- Signature + Signature Signature
} - +
@if (fetchingLinkedData) { @@ -198,7 +252,11 @@
Date of Birth - {{ familyMemberForm.get('dateOfBirth')?.value ? (familyMemberForm.get('dateOfBirth')?.value | date:'dd MMM yyyy') : '—' }} + {{ + familyMemberForm.get('dateOfBirth')?.value + ? (familyMemberForm.get('dateOfBirth')?.value | date: 'dd MMM yyyy') + : '—' + }}
Aadhaar @@ -292,8 +350,13 @@ - + PDF, JPG or PNG (max 5 MB) } @else {
@@ -313,15 +376,32 @@
Aadhaar Number - + PAN Number - - @if (familyMemberForm.get('panNumber')?.hasError('pattern') && familyMemberForm.get('panNumber')?.touched) { + + @if ( + familyMemberForm.get('panNumber')?.hasError('pattern') && familyMemberForm.get('panNumber')?.touched + ) { Invalid PAN format } @@ -348,9 +428,13 @@ } @else { - } diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss index 1cd1dc0fe..90aa50ae5 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss @@ -1,3 +1,11 @@ +/** + * 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/. + */ + .fm-dialog__header { display: flex; align-items: center; @@ -9,7 +17,9 @@ font-weight: 600; color: #1e293b; - .fm-dialog__icon { color: #1074b9; } + .fm-dialog__icon { + color: #1074b9; + } } .fm-mode-toggle { @@ -32,7 +42,11 @@ cursor: pointer; transition: all 0.15s; - mat-icon { font-size: 18px; width: 18px; height: 18px; } + mat-icon { + font-size: 18px; + width: 18px; + height: 18px; + } &.active { border-color: #1074b9; @@ -40,11 +54,19 @@ color: #1074b9; } - &:hover:not(.active) { border-color: #94a3b8; } + &:hover:not(.active) { + border-color: #94a3b8; + } } -.fm-search-wrapper { position: relative; margin-bottom: 12px; } -.fm-search-field { width: 100%; } +.fm-search-wrapper { + position: relative; + margin-bottom: 12px; +} + +.fm-search-field { + width: 100%; +} .fm-search-results { position: absolute; @@ -55,7 +77,7 @@ background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + box-shadow: 0 8px 24px rgb(0 0 0 / 12%); max-height: 220px; overflow-y: auto; } @@ -92,13 +114,34 @@ cursor: pointer; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } - &:hover { background: #f0f7ff; } + &:last-child { + border-bottom: none; + } - &__icon { color: #1074b9; font-size: 20px; } - &__info { display: flex; flex-direction: column; } - &__name { font-size: 13px; font-weight: 600; color: #1e293b; } - &__meta { font-size: 11px; color: #64748b; } + &:hover { + background: #f0f7ff; + } + + &__icon { + color: #1074b9; + font-size: 20px; + } + + &__info { + display: flex; + flex-direction: column; + } + + &__name { + font-size: 13px; + font-weight: 600; + color: #1e293b; + } + + &__meta { + font-size: 11px; + color: #64748b; + } } .fm-linked-card { @@ -111,8 +154,14 @@ border-radius: 8px; margin-bottom: 16px; - &__left { flex-shrink: 0; } - &__icon { color: #1074b9; } + &__left { + flex-shrink: 0; + } + + &__icon { + color: #1074b9; + } + &__photo { width: 40px; height: 40px; @@ -120,9 +169,25 @@ object-fit: cover; border: 2px solid #e2e8f0; } - &__info { flex: 1; display: flex; flex-direction: column; } - &__name { font-size: 14px; font-weight: 600; color: #1e293b; } - &__meta { font-size: 12px; color: #64748b; font-family: 'Roboto Mono', monospace; } + + &__info { + flex: 1; + display: flex; + flex-direction: column; + } + + &__name { + font-size: 14px; + font-weight: 600; + color: #1e293b; + } + + &__meta { + font-size: 12px; + color: #64748b; + font-family: 'Roboto Mono', monospace; + } + &__sig { display: flex; flex-direction: column; @@ -139,6 +204,7 @@ background: #fff; padding: 2px 4px; } + span { font-size: 9px; color: #94a3b8; @@ -146,7 +212,14 @@ letter-spacing: 0.04em; } } - &__remove { color: #94a3b8 !important; &:hover { color: #ef4444 !important; } } + + &__remove { + color: #94a3b8 !important; + + &:hover { + color: #ef4444 !important; + } + } } .fm-fetching-hint { @@ -201,8 +274,16 @@ padding: 10px 16px; margin-bottom: 8px; - .fm-readonly-label { display: block; margin-bottom: 4px; } - .fm-readonly-value { font-size: 13px; color: #475569; line-height: 1.5; } + .fm-readonly-label { + display: block; + margin-bottom: 4px; + } + + .fm-readonly-value { + font-size: 13px; + color: #475569; + line-height: 1.5; + } } .fm-kyc-badge { @@ -225,6 +306,7 @@ background: #dcfce7; color: #16a34a; } + &.not-verified { background: #fef3c7; color: #d97706; @@ -262,16 +344,22 @@ .zoomable { cursor: zoom-in; - transition: transform 0.15s ease, box-shadow 0.15s ease; + transition: + transform 0.15s ease, + box-shadow 0.15s ease; &:hover { transform: scale(1.08); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); + box-shadow: 0 4px 16px rgb(0 0 0 / 18%); } } ::ng-deep .image-zoom-panel { - .mat-mdc-dialog-container { padding: 0 !important; border-radius: 10px !important; overflow: hidden; } + .mat-mdc-dialog-container { + padding: 0 !important; + border-radius: 10px !important; + overflow: hidden; + } } .fm-file-upload { @@ -290,7 +378,11 @@ color: #1074b9; border-color: #cbd5e1; - mat-icon { font-size: 18px; width: 18px; height: 18px; } + mat-icon { + font-size: 18px; + width: 18px; + height: 18px; + } } .fm-file-hint { @@ -308,7 +400,11 @@ border-radius: 6px; width: 100%; - &__icon { color: #1074b9; font-size: 20px; } + &__icon { + color: #1074b9; + font-size: 20px; + } + &__name { flex: 1; font-size: 13px; @@ -318,13 +414,21 @@ overflow: hidden; text-overflow: ellipsis; } + &__remove { color: #94a3b8 !important; width: 28px !important; height: 28px !important; line-height: 28px !important; - mat-icon { font-size: 16px; width: 16px; height: 16px; } - &:hover { color: #ef4444 !important; } + mat-icon { + font-size: 16px; + width: 16px; + height: 16px; + } + + &:hover { + color: #ef4444 !important; + } } } diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts index 883a3f56e..bc326ad04 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts @@ -87,7 +87,9 @@ export class ClientFamilyMemberDialogComponent implements OnInit { } /** Whether dialog is in View-only mode */ - get isViewMode(): boolean { return this.data.context === 'View'; } + get isViewMode(): boolean { + return this.data.context === 'View'; + } /** Get relationship name from ID */ get relationshipName(): string { @@ -109,7 +111,7 @@ export class ClientFamilyMemberDialogComponent implements OnInit { /** Get marriage authority display label */ getMarriageAuthorityLabel(value: string): string { - return this.marriageAuthorityOptions.find(a => a.value === value)?.label || value || '—'; + return this.marriageAuthorityOptions.find((a) => a.value === value)?.label || value || '—'; } /** Marriage certificate file */ @@ -150,7 +152,10 @@ export class ClientFamilyMemberDialogComponent implements OnInit { }); if (m.linkedClientId) { this.entryMode = 'linked'; - this.selectedClient = { id: m.linkedClientId, displayName: m.linkedClientName || m.firstName || `Client #${m.linkedClientId}` }; + this.selectedClient = { + id: m.linkedClientId, + displayName: m.linkedClientName || m.firstName || `Client #${m.linkedClientId}` + }; // Re-fetch linked client details (profile, signature, KYC status) this.fetchLinkedClientData(m.linkedClientId.toString()); } @@ -162,13 +167,22 @@ export class ClientFamilyMemberDialogComponent implements OnInit { createFamilyMemberForm() { this.familyMemberForm = this.formBuilder.group({ - firstName: ['', Validators.required], - relationshipId: ['', Validators.required], + firstName: [ + '', + Validators.required + ], + relationshipId: [ + '', + Validators.required + ], dateOfBirth: [''], isDependent: [false], remarks: [''], aadhaarNumber: [''], - panNumber: ['', Validators.pattern(/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/)], + panNumber: [ + '', + Validators.pattern(/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/) + ], nationality: ['INDIAN'], isAlive: [true], marriageDate: [''], @@ -208,7 +222,10 @@ export class ClientFamilyMemberDialogComponent implements OnInit { this.searching = false; this.clientsService.enrichSearchResults(this.searchResults); }, - error: () => { this.searchResults = []; this.searching = false; } + error: () => { + this.searchResults = []; + this.searching = false; + } }); }, 400); } @@ -242,7 +259,9 @@ export class ClientFamilyMemberDialogComponent implements OnInit { this.setLinkedFieldsReadonly(true); this.fetchingLinkedData = false; }, - error: () => { this.fetchingLinkedData = false; } + error: () => { + this.fetchingLinkedData = false; + } }); // Fetch KYC (PAN, Aadhaar, verification status) @@ -250,8 +269,12 @@ export class ClientFamilyMemberDialogComponent implements OnInit { next: (kyc: any) => { if (kyc) { const patch: any = {}; - if (kyc.panNumber) { patch.panNumber = kyc.panNumber; } - if (kyc.aadhaarNumberMasked) { patch.aadhaarNumber = kyc.aadhaarNumberMasked; } + if (kyc.panNumber) { + patch.panNumber = kyc.panNumber; + } + if (kyc.aadhaarNumberMasked) { + patch.aadhaarNumber = kyc.aadhaarNumberMasked; + } this.familyMemberForm.patchValue(patch); this.linkedKycStatus = { panVerified: kyc.panVerified || false, @@ -278,8 +301,8 @@ export class ClientFamilyMemberDialogComponent implements OnInit { // Fetch signature image this.clientsService.getClientDocuments(clientId).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => - d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') ); if (sigDoc) { this.clientsService.getClientSignatureImage(clientId, sigDoc.id.toString()).subscribe({ @@ -296,8 +319,13 @@ export class ClientFamilyMemberDialogComponent implements OnInit { /** Disable/enable fields that are auto-populated from linked client */ private setLinkedFieldsReadonly(readonly: boolean): void { - const fields = ['firstName', 'dateOfBirth', 'aadhaarNumber', 'panNumber']; - fields.forEach(f => { + const fields = [ + 'firstName', + 'dateOfBirth', + 'aadhaarNumber', + 'panNumber' + ]; + fields.forEach((f) => { const ctrl = this.familyMemberForm.get(f); if (ctrl) { readonly ? ctrl.disable() : ctrl.enable(); @@ -313,7 +341,11 @@ export class ClientFamilyMemberDialogComponent implements OnInit { this.linkedKycStatus = null; this.setLinkedFieldsReadonly(false); this.familyMemberForm.patchValue({ - firstName: '', linkedClientId: '', dateOfBirth: '', aadhaarNumber: '', panNumber: '' + firstName: '', + linkedClientId: '', + dateOfBirth: '', + aadhaarNumber: '', + panNumber: '' }); } @@ -385,26 +417,49 @@ export class ClientFamilyMemberDialogComponent implements OnInit {
`, - styles: [` - .img-zoom-dialog { display: flex; flex-direction: column; } - .img-zoom-header { - display: flex; align-items: center; justify-content: space-between; - padding: 10px 16px; background: #f8fafc; border-bottom: 1px solid #e2e8f0; - font-size: 14px; font-weight: 600; color: #1e293b; - } - .img-zoom-body { - display: flex; align-items: center; justify-content: center; - padding: 20px; background: #fff; min-height: 200px; - } - .img-zoom-body img { - max-width: 100%; max-height: 70vh; object-fit: contain; - border-radius: 6px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); - } - `], - imports: [MatIcon, STANDALONE_SHARED_IMPORTS] + styles: [ + ` + .img-zoom-dialog { + display: flex; + flex-direction: column; + } + .img-zoom-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 16px; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + font-size: 14px; + font-weight: 600; + color: #1e293b; + } + .img-zoom-body { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + background: #fff; + min-height: 200px; + } + .img-zoom-body img { + max-width: 100%; + max-height: 70vh; + object-fit: contain; + border-radius: 6px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); + } + ` + ], + imports: [ + MatIcon, + STANDALONE_SHARED_IMPORTS + ] }) export class ImageZoomDialogComponent { dialogRef = inject>(MatDialogRef); data = inject(MAT_DIALOG_DATA); - close() { this.dialogRef.close(); } + close() { + this.dialogRef.close(); + } } diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html index 9251015f7..aab5f1fb6 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html @@ -1,3 +1,11 @@ + + @@ -75,9 +83,13 @@ {{ member.dateOfBirth ? (member.dateOfBirth | dateFormat) : '—' }} - @if (member.panNumber) { {{ member.panNumber }} } - @else if (member.aadhaarNumber) { {{ member.aadhaarNumber }} } - @else { — } + @if (member.panNumber) { + {{ member.panNumber }} + } @else if (member.aadhaarNumber) { + {{ member.aadhaarNumber }} + } @else { + — + } @if (member.isAlive !== false) { @@ -87,14 +99,32 @@ } - @if (!readOnly) { - - } @@ -103,5 +133,4 @@ }
} -
diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss index fbea465ae..0bdf7fb98 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss @@ -1,11 +1,29 @@ +/** + * 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/. + */ + .zoomable { cursor: zoom-in; - transition: transform 0.15s ease, box-shadow 0.15s ease; - &:hover { transform: scale(1.08); box-shadow: 0 4px 16px rgba(0,0,0,0.18); } + transition: + transform 0.15s ease, + box-shadow 0.15s ease; + + &:hover { + transform: scale(1.08); + box-shadow: 0 4px 16px rgb(0 0 0 / 18%); + } } ::ng-deep .image-zoom-panel { - .mat-mdc-dialog-container { padding: 0 !important; border-radius: 10px !important; overflow: hidden; } + .mat-mdc-dialog-container { + padding: 0 !important; + border-radius: 10px !important; + overflow: hidden; + } } .search-no-results-hint { @@ -42,7 +60,7 @@ &__sub { font-size: 11px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); margin: 2px 0 0; } } @@ -63,7 +81,10 @@ color: #1e293b; } -.fm-step__icon { color: #1074b9; font-size: 14px; } +.fm-step__icon { + color: #1074b9; + font-size: 14px; +} .fm-step__count { background: #1074b9; @@ -170,8 +191,13 @@ border-bottom: 1px solid #f1f5f9; transition: background 0.1s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } .fm-table__col { @@ -186,11 +212,31 @@ gap: 8px; min-width: 0; } - &--rel { flex: 0 0 130px; } - &--dob { flex: 0 0 120px; color: #64748b; font-size: 12px; } - &--id { flex: 0 0 140px; } - &--status { flex: 0 0 60px; text-align: center; } - &--actions { flex: 0 0 80px; display: flex; gap: 2px; } + + &--rel { + flex: 0 0 130px; + } + + &--dob { + flex: 0 0 120px; + color: #64748b; + font-size: 12px; + } + + &--id { + flex: 0 0 140px; + } + + &--status { + flex: 0 0 60px; + text-align: center; + } + + &--actions { + flex: 0 0 80px; + display: flex; + gap: 2px; + } } .fm-table__avatar { @@ -232,8 +278,15 @@ padding: 2px 8px; border-radius: 10px; - &--yes { background: #ecfdf5; color: #059669; } - &--no { background: #fef2f2; color: #dc2626; } + &--yes { + background: #ecfdf5; + color: #059669; + } + + &--no { + background: #fef2f2; + color: #dc2626; + } } .fm-action-btn { @@ -248,11 +301,16 @@ color: #64748b; } - &:hover mat-icon { color: #1074b9; } - &--delete:hover mat-icon { color: #ef4444; } + &:hover mat-icon { + color: #1074b9; + } + + &--delete:hover mat-icon { + color: #ef4444; + } } -@media (max-width: 768px) { +@media (width <= 768px) { .fm-step__header { flex-direction: column; align-items: flex-start; @@ -260,7 +318,10 @@ } .fm-table__col { - &--type, &--id { display: none; } + &--type, + &--id { + display: none; + } } } @@ -284,11 +345,27 @@ gap: 10px; } -.nom-header__icon { font-size: 16px; color: #1565c0; } -.nom-header__title { font-size: 16px; font-weight: 600; color: #1e293b; display: block; } -.nom-header__sub { font-size: 11px; color: #94a3b8; display: block; } +.nom-header__icon { + font-size: 16px; + color: #1565c0; +} -.nom-add-btn { font-size: 13px; } +.nom-header__title { + font-size: 16px; + font-weight: 600; + color: #1e293b; + display: block; +} + +.nom-header__sub { + font-size: 11px; + color: #94a3b8; + display: block; +} + +.nom-add-btn { + font-size: 13px; +} /* Share bar */ .nom-share-bar { @@ -302,7 +379,11 @@ border-radius: 6px; } -.nom-share-bar__label { font-size: 12px; color: #64748b; white-space: nowrap; } +.nom-share-bar__label { + font-size: 12px; + color: #64748b; + white-space: nowrap; +} .nom-share-bar__track { flex: 1; @@ -317,8 +398,14 @@ background: #1565c0; border-radius: 4px; transition: width 0.3s; - &.complete { background: #059669; } - &.over { background: #dc2626; } + + &.complete { + background: #059669; + } + + &.over { + background: #dc2626; + } } .nom-share-bar__value { @@ -327,7 +414,10 @@ color: #1565c0; min-width: 36px; text-align: right; - &.over { color: #dc2626; } + + &.over { + color: #dc2626; + } } /* Nominee card */ @@ -346,23 +436,62 @@ } .nom-card__avatar { - width: 36px; height: 36px; + width: 36px; + height: 36px; border-radius: 50%; background: #dbeafe; - display: flex; align-items: center; justify-content: center; + display: flex; + align-items: center; + justify-content: center; color: #1565c0; - mat-icon { font-size: 20px; } + + mat-icon { + font-size: 20px; + } +} + +.nom-card__photo { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.nom-card__info { + flex: 1; +} + +.nom-card__name { + font-weight: 600; + font-size: 14px; + color: #1e293b; + display: block; +} + +.nom-card__meta { + font-size: 12px; + color: #64748b; + display: block; + margin-top: 2px; } -.nom-card__photo { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; } -.nom-card__info { flex: 1; } -.nom-card__name { font-weight: 600; font-size: 14px; color: #1e293b; display: block; } -.nom-card__meta { font-size: 12px; color: #64748b; display: block; margin-top: 2px; } .nom-card__sig { flex-shrink: 0; - img { height: 24px; max-width: 60px; object-fit: contain; border: 1px solid #e2e8f0; border-radius: 4px; padding: 2px; } + + img { + height: 24px; + max-width: 60px; + object-fit: contain; + border: 1px solid #e2e8f0; + border-radius: 4px; + padding: 2px; + } +} + +.nom-card__actions { + display: flex; + gap: 2px; } -.nom-card__actions { display: flex; gap: 2px; } .nom-card__edit { padding: 12px 16px; @@ -391,8 +520,18 @@ min-width: 50px; } -.nom-card__share-value { font-size: 18px; font-weight: 700; color: #1565c0; display: block; } -.nom-card__share-label { font-size: 10px; color: #94a3b8; text-transform: uppercase; } +.nom-card__share-value { + font-size: 18px; + font-weight: 700; + color: #1565c0; + display: block; +} + +.nom-card__share-label { + font-size: 10px; + color: #94a3b8; + text-transform: uppercase; +} .nom-card__guardian { display: flex; @@ -403,7 +542,13 @@ border-top: 1px solid #e0f2fe; font-size: 12px; color: #0c4a6e; - mat-icon { font-size: 16px; width: 16px; height: 16px; color: #0ea5e9; } + + mat-icon { + font-size: 16px; + width: 16px; + height: 16px; + color: #0ea5e9; + } } /* Empty */ @@ -420,8 +565,16 @@ font-size: 14px; } -.nom-empty__icon { font-size: 36px; width: 36px; height: 36px; color: #cbd5e1; } -.nom-empty__sub { font-size: 12px; } +.nom-empty__icon { + font-size: 36px; + width: 36px; + height: 36px; + color: #cbd5e1; +} + +.nom-empty__sub { + font-size: 12px; +} /* Nominee form */ .nom-form { @@ -443,33 +596,47 @@ } /* Search */ -.nom-search-wrapper { position: relative; } -.nom-search-field { width: 100%; } +.nom-search-wrapper { + position: relative; +} + +.nom-search-field { + width: 100%; +} + .nom-search-prefix { display: inline-flex; align-items: center; padding-right: 8px; - mat-icon { font-size: 20px; color: #94a3b8; } + + mat-icon { + font-size: 20px; + color: #94a3b8; + } } .nom-search-results { position: absolute; top: 100%; - left: 0; right: 0; + left: 0; + right: 0; z-index: 10; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; - box-shadow: 0 4px 12px rgba(0,0,0,0.1); + box-shadow: 0 4px 12px rgb(0 0 0 / 10%); max-height: 240px; overflow-y: auto; margin-top: -12px; } .nom-search-loading { - display: flex; align-items: center; gap: 8px; + display: flex; + align-items: center; + gap: 8px; padding: 12px 16px; - font-size: 13px; color: #64748b; + font-size: 13px; + color: #64748b; } .nom-search-item { @@ -478,14 +645,43 @@ gap: 10px; padding: 10px 16px; cursor: pointer; - &:hover { background: #f1f5f9; } + + &:hover { + background: #f1f5f9; + } } -.nom-search-item__icon { color: #94a3b8; font-size: 20px; } -.nom-search-item__info { flex: 1; } -.nom-search-item__name { font-weight: 500; font-size: 13px; color: #1e293b; display: block; } -.nom-search-item__meta { font-size: 11px; color: #94a3b8; display: block; } -.nom-search-item__status { font-size: 11px; font-weight: 600; color: #94a3b8; &.active { color: #22c55e; } } +.nom-search-item__icon { + color: #94a3b8; + font-size: 20px; +} + +.nom-search-item__info { + flex: 1; +} + +.nom-search-item__name { + font-weight: 500; + font-size: 13px; + color: #1e293b; + display: block; +} + +.nom-search-item__meta { + font-size: 11px; + color: #94a3b8; + display: block; +} + +.nom-search-item__status { + font-size: 11px; + font-weight: 600; + color: #94a3b8; + + &.active { + color: #22c55e; + } +} /* Selected nominee */ .nom-selected { @@ -505,30 +701,92 @@ } .nom-selected__avatar { - width: 40px; height: 40px; + width: 40px; + height: 40px; border-radius: 50%; background: #dbeafe; - display: flex; align-items: center; justify-content: center; + display: flex; + align-items: center; + justify-content: center; color: #1565c0; overflow: hidden; } -.nom-selected__photo { width: 100%; height: 100%; object-fit: cover; } -.nom-selected__info { flex: 1; } -.nom-selected__name { font-weight: 600; font-size: 14px; color: #1e293b; display: block; } -.nom-selected__meta { font-size: 12px; color: #64748b; display: block; } +.nom-selected__photo { + width: 100%; + height: 100%; + object-fit: cover; +} + +.nom-selected__info { + flex: 1; +} + +.nom-selected__name { + font-weight: 600; + font-size: 14px; + color: #1e293b; + display: block; +} + +.nom-selected__meta { + font-size: 12px; + color: #64748b; + display: block; +} + .nom-selected__sig { - display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; - img { height: 28px; max-width: 70px; object-fit: contain; border: 1px solid #e2e8f0; border-radius: 4px; background: #fff; padding: 2px 4px; } - span { font-size: 9px; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.04em; } + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + flex-shrink: 0; + + img { + height: 28px; + max-width: 70px; + object-fit: contain; + border: 1px solid #e2e8f0; + border-radius: 4px; + background: #fff; + padding: 2px 4px; + } + + span { + font-size: 9px; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.04em; + } +} + +.nom-selected__remove { + color: #94a3b8; +} + +.nom-selected__loading { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + font-size: 12px; + color: #64748b; } -.nom-selected__remove { color: #94a3b8; } -.nom-selected__loading { display: flex; align-items: center; gap: 8px; padding: 12px 16px; font-size: 12px; color: #64748b; } .nom-kyc-badge { - display: inline-flex; align-items: center; margin-left: 4px; - mat-icon { font-size: 14px; width: 14px; height: 14px; } - &.verified { color: #16a34a; } + display: inline-flex; + align-items: center; + margin-left: 4px; + + mat-icon { + font-size: 14px; + width: 14px; + height: 14px; + } + + &.verified { + color: #16a34a; + } } .nom-selected__details { @@ -539,10 +797,29 @@ flex-wrap: wrap; } -.nom-detail { display: flex; flex-direction: column; gap: 2px; } -.nom-detail__label { font-size: 10px; text-transform: uppercase; color: #94a3b8; letter-spacing: 0.5px; } -.nom-detail__value { font-size: 13px; font-weight: 500; color: #1e293b; } -.nom-detail__value.mono { font-family: 'Roboto Mono', monospace; font-size: 12px; } +.nom-detail { + display: flex; + flex-direction: column; + gap: 2px; +} + +.nom-detail__label { + font-size: 10px; + text-transform: uppercase; + color: #94a3b8; + letter-spacing: 0.5px; +} + +.nom-detail__value { + font-size: 13px; + font-weight: 500; + color: #1e293b; +} + +.nom-detail__value.mono { + font-family: 'Roboto Mono', monospace; + font-size: 12px; +} /* Form fields */ .nom-form-fields { @@ -553,8 +830,13 @@ align-items: center; } -.nom-form-field { width: 100%; } -.nom-minor-toggle { padding-top: 8px; } +.nom-form-field { + width: 100%; +} + +.nom-minor-toggle { + padding-top: 8px; +} /* Guardian */ .nom-guardian-section { @@ -570,7 +852,11 @@ font-weight: 600; color: #0c4a6e; margin-bottom: 10px; - mat-icon { font-size: 18px; color: #0ea5e9; } + + mat-icon { + font-size: 18px; + color: #0ea5e9; + } } .nom-guardian-card { @@ -581,13 +867,32 @@ background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 8px; - mat-icon { color: #0ea5e9; } + + mat-icon { + color: #0ea5e9; + } } -.nom-guardian-card__info { flex: 1; } -.nom-guardian-card__name { font-weight: 600; font-size: 13px; color: #1e293b; display: block; } -.nom-guardian-card__meta { font-size: 11px; color: #64748b; display: block; } -.nom-guardian-rel { width: 160px; } +.nom-guardian-card__info { + flex: 1; +} + +.nom-guardian-card__name { + font-weight: 600; + font-size: 13px; + color: #1e293b; + display: block; +} + +.nom-guardian-card__meta { + font-size: 11px; + color: #64748b; + display: block; +} + +.nom-guardian-rel { + width: 160px; +} /* Actions */ .nom-form-actions { @@ -596,11 +901,24 @@ gap: 10px; padding: 12px 16px; border-top: 1px solid #f1f5f9; - button { display: flex; align-items: center; gap: 6px; } + + button { + display: flex; + align-items: center; + gap: 6px; + } } -@media (max-width: 768px) { - .nom-form-fields { grid-template-columns: 1fr; } - .nom-guardian-card { flex-wrap: wrap; } - .nom-guardian-rel { width: 100%; } +@media (width <= 768px) { + .nom-form-fields { + grid-template-columns: 1fr; + } + + .nom-guardian-card { + flex-wrap: wrap; + } + + .nom-guardian-rel { + width: 100%; + } } diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts index bd456cbab..07132b599 100644 --- a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts @@ -1,5 +1,9 @@ /** * 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, Input, OnInit, inject } from '@angular/core'; @@ -88,9 +92,20 @@ export class ClientFamilyMembersStepComponent implements OnInit { /** Relationship options */ relationshipOptions = [ - 'Father', 'Mother', 'Spouse', 'Son', 'Daughter', - 'Brother', 'Sister', 'Uncle', 'Aunt', 'Grandfather', - 'Grandmother', 'Friend', 'Business Partner', 'Other' + 'Father', + 'Mother', + 'Spouse', + 'Son', + 'Daughter', + 'Brother', + 'Sister', + 'Uncle', + 'Aunt', + 'Grandfather', + 'Grandmother', + 'Friend', + 'Business Partner', + 'Other' ]; ngOnInit(): void { @@ -109,14 +124,18 @@ export class ClientFamilyMembersStepComponent implements OnInit { this.loaded = true; this.clientFamilyMembers = Array.isArray(data) ? data : []; }, - error: () => { this.loaded = true; } + error: () => { + this.loaded = true; + } }); } addFamilyMember() { const ref = this.dialog.open(ClientFamilyMemberDialogComponent, { data: { context: 'Add', options: this.clientTemplate.familyMemberOptions }, - width: '640px', maxWidth: '95vw', panelClass: 'fm-dialog-panel' + width: '640px', + maxWidth: '95vw', + panelClass: 'fm-dialog-panel' }); ref.afterClosed().subscribe((response: any) => { if (response?.member) { @@ -128,14 +147,18 @@ export class ClientFamilyMembersStepComponent implements OnInit { viewFamilyMember(member: any) { this.dialog.open(ClientFamilyMemberDialogComponent, { data: { context: 'View', member, options: this.clientTemplate.familyMemberOptions }, - width: '640px', maxWidth: '95vw', panelClass: 'fm-dialog-panel' + width: '640px', + maxWidth: '95vw', + panelClass: 'fm-dialog-panel' }); } editFamilyMember(member: any, index: number) { const ref = this.dialog.open(ClientFamilyMemberDialogComponent, { data: { context: 'Edit', member, options: this.clientTemplate.familyMemberOptions }, - width: '640px', maxWidth: '95vw', panelClass: 'fm-dialog-panel' + width: '640px', + maxWidth: '95vw', + panelClass: 'fm-dialog-panel' }); ref.afterClosed().subscribe((response: any) => { if (response?.member) { @@ -175,8 +198,7 @@ export class ClientFamilyMembersStepComponent implements OnInit { return this.clientFamilyMembers.some((m: any) => { const rel = m.relationship || m.relationshipId; const relName = typeof rel === 'string' ? rel : ''; - return relName.toLowerCase() === 'spouse' || - m.firstName?.toLowerCase().includes('spouse'); + return relName.toLowerCase() === 'spouse' || m.firstName?.toLowerCase().includes('spouse'); }); } @@ -191,14 +213,19 @@ export class ClientFamilyMembersStepComponent implements OnInit { const allMembers = Array.isArray(data) ? data : []; this.nominees = allMembers.filter((m: any) => m.nominationType && m.nominationType !== ''); }, - error: () => { this.nomineesLoaded = true; } + error: () => { + this.nomineesLoaded = true; + } }); } onNomineeSearch(text: string): void { this.nomineeSearchText = text; clearTimeout(this.nomineeSearchTimer); - if (!text || text.length < 2) { this.nomineeSearchResults = []; return; } + if (!text || text.length < 2) { + this.nomineeSearchResults = []; + return; + } this.nomineeSearching = true; this.nomineeSearchTimer = setTimeout(() => { this.clientsService.searchByText(text, 0, 10).subscribe({ @@ -207,7 +234,10 @@ export class ClientFamilyMembersStepComponent implements OnInit { this.nomineeSearching = false; this.clientsService.enrichSearchResults(this.nomineeSearchResults); }, - error: () => { this.nomineeSearchResults = []; this.nomineeSearching = false; } + error: () => { + this.nomineeSearchResults = []; + this.nomineeSearching = false; + } }); }, 400); } @@ -232,7 +262,9 @@ export class ClientFamilyMembersStepComponent implements OnInit { // Fetch client data for DOB this.clientsService.getClientData(clientId.toString()).subscribe({ next: (data: any) => { - if (data?.dateOfBirth) { details.dateOfBirth = data.dateOfBirth; } + if (data?.dateOfBirth) { + details.dateOfBirth = data.dateOfBirth; + } done(); }, error: () => done() @@ -253,7 +285,9 @@ export class ClientFamilyMembersStepComponent implements OnInit { // Fetch profile image this.clientsService.getClientProfileImage(clientId.toString()).subscribe({ next: (img: any) => { - if (img) { details.profileImageUrl = this.sanitizer.bypassSecurityTrustResourceUrl(img); } + if (img) { + details.profileImageUrl = this.sanitizer.bypassSecurityTrustResourceUrl(img); + } done(); }, error: () => done() @@ -262,8 +296,8 @@ export class ClientFamilyMembersStepComponent implements OnInit { // Fetch signature image this.clientsService.getClientDocuments(clientId.toString()).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => - d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') ); if (sigDoc) { this.clientsService.getClientSignatureImage(clientId.toString(), sigDoc.id.toString()).subscribe({ @@ -273,7 +307,9 @@ export class ClientFamilyMembersStepComponent implements OnInit { }, error: () => done() }); - } else { done(); } + } else { + done(); + } }, error: () => done() }); @@ -282,7 +318,10 @@ export class ClientFamilyMembersStepComponent implements OnInit { onGuardianSearch(text: string): void { this.guardianSearchText = text; clearTimeout(this.guardianSearchTimer); - if (!text || text.length < 2) { this.guardianSearchResults = []; return; } + if (!text || text.length < 2) { + this.guardianSearchResults = []; + return; + } this.guardianSearching = true; this.guardianSearchTimer = setTimeout(() => { this.clientsService.searchByText(text, 0, 10).subscribe({ @@ -291,7 +330,10 @@ export class ClientFamilyMembersStepComponent implements OnInit { this.guardianSearching = false; this.clientsService.enrichSearchResults(this.guardianSearchResults); }, - error: () => { this.guardianSearchResults = []; this.guardianSearching = false; } + error: () => { + this.guardianSearchResults = []; + this.guardianSearching = false; + } }); }, 400); } @@ -315,7 +357,8 @@ export class ClientFamilyMembersStepComponent implements OnInit { } saveNominee(): void { - if (!this.clientId || !this.selectedNomineeClient || !this.nomineeRelationshipId || !this.nomineePercentageShare) return; + if (!this.clientId || !this.selectedNomineeClient || !this.nomineeRelationshipId || !this.nomineePercentageShare) + return; if (this.totalSharePercentage > 100) return; this.nomineeSaving = true; @@ -360,7 +403,9 @@ export class ClientFamilyMembersStepComponent implements OnInit { }); this.resetNomineeForm(); }, - error: () => { this.nomineeSaving = false; } + error: () => { + this.nomineeSaving = false; + } }); } @@ -383,13 +428,13 @@ export class ClientFamilyMembersStepComponent implements OnInit { /** Max share allowed for the nominee being edited (excludes its own share) */ maxShareForEdit(index: number): number { - const otherTotal = this.nominees.reduce((sum, n, i) => i === index ? sum : sum + (n.percentageShare || 0), 0); + const otherTotal = this.nominees.reduce((sum, n, i) => (i === index ? sum : sum + (n.percentageShare || 0)), 0); return 100 - otherTotal; } saveEditNominee(nominee: any, index: number): void { if (!this.editNomineeRelId || !this.editNomineeShare) return; - const otherTotal = this.nominees.reduce((sum, n, i) => i === index ? sum : sum + (n.percentageShare || 0), 0); + const otherTotal = this.nominees.reduce((sum, n, i) => (i === index ? sum : sum + (n.percentageShare || 0)), 0); if (otherTotal + this.editNomineeShare > 100) return; const relOption = this.clientTemplate?.familyMemberOptions?.relationshipIdOptions?.find( @@ -413,8 +458,12 @@ export class ClientFamilyMembersStepComponent implements OnInit { dateFormat: 'dd MMMM yyyy', locale: 'en' }; - if (nominee.guardianName) { payload.guardianName = nominee.guardianName; } - if (nominee.guardianRelationship) { payload.guardianRelationship = nominee.guardianRelationship; } + if (nominee.guardianName) { + payload.guardianName = nominee.guardianName; + } + if (nominee.guardianRelationship) { + payload.guardianRelationship = nominee.guardianRelationship; + } this.clientsService.editFamilyMember(this.clientId, nominee.id, payload).subscribe({ error: () => {} }); } diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.html b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html index aeb05edca..e1d044793 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.html +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html @@ -42,7 +42,9 @@ @if (createClientForm.get('legalFormId')?.value) { - {{ createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business' }} + {{ + createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business' + }} {{ getSelectedLegalFormLabel() }} } @@ -67,12 +69,12 @@ @if (getSelectedNationality().flagType === 'india') { - - - - - - + + + + + + } @else { @@ -88,12 +90,12 @@ @if (nat.flagType === 'india') { - - - - - - + + + + + + } @else { @@ -129,7 +131,10 @@ @if (createClientForm.get('fullname')) { - {{ 'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name']) | translate }} + {{ + 'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name']) + | translate + }} @if (createClientForm.controls.fullname.hasError('required')) { @@ -150,30 +155,39 @@
@if (!sanctionsVerified && !sanctionsBlocked) { - } @if (sanctionsVerified) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 - - Verified - - Verified + info + matTooltipClass="sanctions-tooltip" + >info } @if (sanctionsBlocked) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 Sanctions Match - info + matTooltipClass="sanctions-tooltip" + >info }
@@ -181,7 +195,9 @@ } - @if (createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname')) { + @if ( + createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname') + ) {
@if (createClientForm.get('salutationId') && salutationOptions?.length > 0) { @@ -213,7 +229,10 @@ {{ 'labels.inputs.Middle Name' | translate }} person_outline - @if (createClientForm.controls.middlename?.hasError('pattern') && createClientForm.controls.middlename?.touched) { + @if ( + createClientForm.controls.middlename?.hasError('pattern') && + createClientForm.controls.middlename?.touched + ) { Only letters, spaces, dots and hyphens allowed } @@ -243,30 +262,39 @@
@if (!sanctionsVerified && !sanctionsBlocked) { - } @if (sanctionsVerified) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 - - Verified - - Verified + info + matTooltipClass="sanctions-tooltip" + >info } @if (sanctionsBlocked) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 Sanctions Match - info + matTooltipClass="sanctions-tooltip" + >info }
@@ -278,10 +306,18 @@ Date of Birth calendar_today - + - @if (createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched) { + @if ( + createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched + ) { Date of Birth is required } @@ -324,7 +360,6 @@ }
} -
@@ -341,9 +376,15 @@
- {{ ageBreakdown.years }} yrs - {{ ageBreakdown.months }} mos - {{ ageBreakdown.days }} days + {{ ageBreakdown.years }} yrs + {{ ageBreakdown.months }} mos + {{ ageBreakdown.days }} days MINOR
@if (!selectedGuardian && !guardianLinking) { @@ -357,13 +398,14 @@
- @if (!selectedGuardian && !guardianLinking) {
person_off
- No guardian linked yet. Click "Link Guardian" to search and add. + No guardian linked yet. Click "Link Guardian" to search and add. Guardian is mandatory for minor accounts
@@ -377,22 +419,33 @@ man Father woman Mother - gavel Legal Guardian - elderly Grandparent + gavel Legal Guardian + elderly Grandparent group Other
- + Search Guardian by Name or Client ID person_search - + placeholder="Select relationship first, then search..." + autocomplete="off" + /> @if (guardianSearching) { @@ -413,11 +466,17 @@
{{ result.displayName }} - ID: {{ result.id }} · A/C: {{ result.accountNumber }} · {{ result.officeName }}@if (result.dateOfBirth) { · DOB: {{ result.dateOfBirth | dateFormat }} }@if (result.fatherSpouseName) { · {{ result.fatherSpouseName }} } + ID: {{ result.id }} · A/C: {{ result.accountNumber }} · + {{ result.officeName }} + @if (result.dateOfBirth) { + · DOB: {{ result.dateOfBirth | dateFormat }} + } + @if (result.fatherSpouseName) { + · {{ result.fatherSpouseName }} + }
- + {{ result.status?.value }}
@@ -429,7 +488,13 @@ }
-
@@ -453,8 +518,12 @@
@if (selectedGuardian.profileImageUrl) { - Photo + Photo } @else { } @@ -470,26 +539,40 @@
- {{ createClientForm.get('guardianDetails.guardianRelation')?.value || '-' }} + {{ + createClientForm.get('guardianDetails.guardianRelation')?.value || '-' + }} - {{ selectedGuardian.accountNo || selectedGuardian.accountNumber || '-' }} + {{ + selectedGuardian.accountNo || selectedGuardian.accountNumber || '-' + }} - + {{ selectedGuardian.status?.value || '-' }} - - @@ -506,16 +589,22 @@ @if (selectedGuardian.panNumber) {
PAN - {{ selectedGuardian.panNumber }} - @if (selectedGuardian.panVerified) { check_circle } + {{ selectedGuardian.panNumber }} + @if (selectedGuardian.panVerified) { + check_circle + }
} @if (selectedGuardian.aadhaarMasked) {
Aadhaar - {{ selectedGuardian.aadhaarMasked }} - @if (selectedGuardian.aadhaarVerified) { check_circle } + {{ selectedGuardian.aadhaarMasked }} + @if (selectedGuardian.aadhaarVerified) { + check_circle + }
} @@ -535,8 +624,12 @@ @if (selectedGuardian.signatureUrl) {
Specimen Signature - Signature + Signature
}
@@ -549,7 +642,7 @@ @if (createClientForm.get('clientNonPersonDetails')) { -
+
Constitution Type @@ -607,7 +700,10 @@
- Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max 10. + Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max + 10.
@@ -619,6 +715,7 @@ Name Client ID PAN + DIN Status Actions
@@ -637,16 +734,45 @@ {{ sig.displayName }} @if (sig._loading) { - + } {{ sig.cifNumber || sig.id }} {{ sig.panNumber || '-' }} - @if (sig.panVerified) { check_circle } + @if (sig.panVerified) { + check_circle + } + + + @if (sig.dinNumber) { + {{ sig.dinNumber }} + @if (sig.dinVerified) { + check_circle + } + @if (!sig.dinVerified) { + cancel + } + } @else { + + } - {{ sig.status?.value || '-' }} + {{ + sig.status?.value || '-' + }}
@@ -716,7 +857,6 @@
- }
@@ -725,9 +865,12 @@

Contact Details - info + matTooltipPosition="above" + >info

@@ -735,21 +878,35 @@ {{ 'labels.inputs.Mobile No' | translate }} phone_android - + (paste)="$event.preventDefault()" + /> @if (createClientForm.get('mobileNo')?.hasError('required') && createClientForm.get('mobileNo')?.touched) { Mobile number is required - } @else if (createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched) { + } @else if ( + createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched + ) { Enter valid 10-digit number (starts with 6-9) }
@if (!mobileVerified) { - @@ -762,19 +919,33 @@ Alternate Mobile No phone_android - - @if (createClientForm.get('alternateMobileNo')?.hasError('pattern') && createClientForm.get('alternateMobileNo')?.touched) { + (paste)="$event.preventDefault()" + /> + @if ( + createClientForm.get('alternateMobileNo')?.hasError('pattern') && + createClientForm.get('alternateMobileNo')?.touched + ) { Enter valid 10-digit number (starts with 6-9) }
@if (!alternateMobileVerified) { - @@ -788,21 +959,27 @@ {{ 'labels.inputs.Email Address' | translate }} email - @if (createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched) { + @if ( + createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched + ) { Email is required } @else if (createClientForm.get('emailAddress')?.invalid && createClientForm.get('emailAddress')?.touched) { Enter a valid email (e.g. name@example.com) } - @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor) { + @if ( + createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor + ) { Marital Status @if (getSelectedMaritalStatus()) { - {{ getMaritalStatusIcon(getSelectedMaritalStatus().name) }} + {{ + getMaritalStatusIcon(getSelectedMaritalStatus().name) + }} {{ getSelectedMaritalStatus().name }} } @@ -823,81 +1000,90 @@ @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) { -
-

- - Financial & Classification -

-
- - - - {{ 'labels.inputs.Occupation Category' | translate }} - - - @if (getSelectedOccupationCategory()) { - - {{ getOccupationCategoryIcon(getSelectedOccupationCategory().name) }} - {{ getSelectedOccupationCategory().name }} - +
+

+ + Financial & Classification +

+
+ + {{ 'labels.inputs.Occupation Category' | translate }} + + + @if (getSelectedOccupationCategory()) { + + {{ + getOccupationCategoryIcon(getSelectedOccupationCategory().name) + }} + {{ getSelectedOccupationCategory().name }} + + } + + @for (cat of occupationCategoryOptions; track cat) { + + + {{ getOccupationCategoryIcon(cat.name) }} + {{ cat.name }} + + } - - @for (cat of occupationCategoryOptions; track cat) { - - - {{ getOccupationCategoryIcon(cat.name) }} - {{ cat.name }} - - - } - - + + - - {{ 'labels.inputs.Occupation' | translate }} - - - @if (getSelectedOccupation()) { - - {{ getOccupationIcon(getSelectedOccupation().name) }} - {{ getSelectedOccupation().name }} - + + {{ 'labels.inputs.Occupation' | translate }} + + + @if (getSelectedOccupation()) { + + {{ getOccupationIcon(getSelectedOccupation().name) }} + {{ getSelectedOccupation().name }} + + } + + @for (occ of occupationOptions; track occ) { + + + {{ getOccupationIcon(occ.name) }} + {{ occ.name }} + + } - - @for (occ of occupationOptions; track occ) { - - - {{ getOccupationIcon(occ.name) }} - {{ occ.name }} - - + + + + + Monthly Income + ₹ + + @if ( + createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched + ) { + Amount cannot be negative } - - + - - Monthly Income - ₹ - - @if (createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched) { - Enter a valid amount - } @else if (createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched) { - Amount cannot be negative - } - - - - Annual Turnover (Last FY) - ₹ - - @if (createClientForm.get('annualTurnover')?.hasError('pattern') && createClientForm.get('annualTurnover')?.touched) { - Enter a valid amount - } @else if (createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched) { - Amount cannot be negative - } - + + Annual Turnover (Last FY) + ₹ + + @if ( + createClientForm.get('annualTurnover')?.hasError('pattern') && + createClientForm.get('annualTurnover')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched + ) { + Amount cannot be negative + } + +
-
} diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss index d00f4ee37..05affe65b 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss @@ -13,7 +13,7 @@ form { .section-block { margin-bottom: 24px; padding-bottom: 16px; - border-bottom: 1px solid rgba(0, 0, 0, 0.06); + border-bottom: 1px solid rgb(0 0 0 / 6%); &:last-child { border-bottom: none; @@ -28,7 +28,7 @@ form { font-size: 15px; font-weight: 600; color: #333; - margin: 0 0 16px 0; + margin: 0 0 16px; .section-icon { color: var(--md-sys-color-primary, #1074b9); @@ -235,7 +235,7 @@ form { border-radius: 12px; overflow: hidden; background: #fff; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + box-shadow: 0 2px 8px rgb(0 0 0 / 4%); } .guardian-section__header { @@ -268,7 +268,7 @@ form { .guardian-section__sub { font-size: 11px; - color: rgba(255, 255, 255, 0.55); + color: rgb(255 255 255 / 55%); } .guardian-section__age-chips { @@ -280,9 +280,9 @@ form { .guardian-age-chip { font-size: 11px; font-weight: 500; - color: rgba(255, 255, 255, 0.8); + color: rgb(255 255 255 / 80%); padding: 3px 10px; - background: rgba(255, 255, 255, 0.1); + background: rgb(255 255 255 / 10%); border-radius: 10px; strong { @@ -408,7 +408,10 @@ form { .guardian-cancel-link { color: #94a3b8 !important; margin-top: 8px; - &:hover { color: #ef4444 !important; } + + &:hover { + color: #ef4444 !important; + } } /* ═══ Guardian Row Table ═══ */ @@ -437,7 +440,10 @@ form { align-items: center; padding: 10px 12px; transition: background 0.1s; - &:hover { background: #f8fafc; } + + &:hover { + background: #f8fafc; + } } .guardian-row__col { @@ -445,12 +451,33 @@ form { font-size: 13px; color: #1e293b; - &--photo { flex: 0 0 50px; } - &--name { flex: 1; min-width: 0; } - &--rel { flex: 0 0 120px; } - &--cif { flex: 0 0 130px; } - &--status { flex: 0 0 80px; } - &--actions { flex: 0 0 110px; display: flex; gap: 2px; justify-content: flex-end; } + &--photo { + flex: 0 0 50px; + } + + &--name { + flex: 1; + min-width: 0; + } + + &--rel { + flex: 0 0 120px; + } + + &--cif { + flex: 0 0 130px; + } + + &--status { + flex: 0 0 80px; + } + + &--actions { + flex: 0 0 110px; + display: flex; + gap: 2px; + justify-content: flex-end; + } } .guardian-row__avatar { @@ -543,14 +570,22 @@ form { color: #64748b; } - &:hover mat-icon { color: #1074b9; } - &--delete:hover mat-icon { color: #ef4444; } + &:hover mat-icon { + color: #1074b9; + } + + &--delete:hover mat-icon { + color: #ef4444; + } } .guardian-row__zoomable { cursor: zoom-in; transition: transform 0.15s; - &:hover { transform: scale(1.05); } + + &:hover { + transform: scale(1.05); + } } /* Expandable detail panel */ @@ -558,12 +593,19 @@ form { border-top: 1px solid #e2e8f0; background: #f8fafc; padding: 16px 20px; - animation: guardianSlideDown 0.2s ease-out; + animation: guardian-slide-down 0.2s ease-out; } -@keyframes guardianSlideDown { - from { opacity: 0; max-height: 0; } - to { opacity: 1; max-height: 500px; } +@keyframes guardian-slide-down { + from { + opacity: 0; + max-height: 0; + } + + to { + opacity: 1; + max-height: 500px; + } } .guardian-expand__grid { @@ -638,12 +680,13 @@ form { padding: 0 !important; border-radius: 12px !important; } + .mdc-dialog__surface { border-radius: 12px !important; } } -@media (max-width: 768px) { +@media (width <= 768px) { .guardian-section__header { flex-direction: column; align-items: flex-start; @@ -665,7 +708,9 @@ form { width: 100%; } - .guardian-row__header { display: none; } + .guardian-row__header { + display: none; + } .guardian-row__data { flex-wrap: wrap; @@ -673,10 +718,23 @@ form { } .guardian-row__col { - &--photo { flex: 0 0 40px; } - &--name { flex: 1 1 200px; } - &--rel, &--cif, &--status { flex: 0 0 auto; } - &--actions { flex: 0 0 auto; } + &--photo { + flex: 0 0 40px; + } + + &--name { + flex: 1 1 200px; + } + + &--rel, + &--cif, + &--status { + flex: 0 0 auto; + } + + &--actions { + flex: 0 0 auto; + } } .guardian-expand__grid { @@ -738,7 +796,7 @@ form { background: #fff; border: 1px solid #e2e8f0; border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + box-shadow: 0 8px 24px rgb(0 0 0 / 12%); max-height: 280px; overflow-y: auto; } @@ -776,8 +834,13 @@ form { transition: background 0.1s; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } - &:hover { background: #f0f7ff; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f0f7ff; + } } .guardian-result-item__avatar { @@ -834,7 +897,7 @@ form { border-radius: 12px; overflow: hidden; background: #fff; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); + box-shadow: 0 1px 4px rgb(0 0 0 / 6%); } .guardian-card__header { @@ -881,7 +944,10 @@ form { .guardian-card__remove { color: #94a3b8 !important; - &:hover { color: #ef4444 !important; } + + &:hover { + color: #ef4444 !important; + } } .guardian-card__body { @@ -898,8 +964,13 @@ form { border-bottom: 1px solid #f1f5f9; border-right: 1px solid #f1f5f9; - &:nth-child(2n) { border-right: none; } - &:nth-last-child(-n+2) { border-bottom: none; } + &:nth-child(2n) { + border-right: none; + } + + &:nth-last-child(-n + 2) { + border-bottom: none; + } } .guardian-card__label { @@ -945,7 +1016,7 @@ form { height: 16px; border-radius: 3px; overflow: hidden; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 0 1px rgb(0 0 0 / 10%); flex-shrink: 0; } @@ -1030,7 +1101,7 @@ form { width: 24px; height: 24px; border: none; - background: rgba(255, 255, 255, 0.15); + background: rgb(255 255 255 / 15%); border-radius: 4px; color: #fff; cursor: pointer; @@ -1040,7 +1111,7 @@ form { font-size: 12px; &:hover { - background: rgba(255, 255, 255, 0.25); + background: rgb(255 255 255 / 25%); } } @@ -1068,10 +1139,22 @@ form { color: #0f172a; } - .row-micro td { background: #ecfdf5; } - .row-small td { background: #e0f2fe; } - .row-medium td { background: #fffbeb; } - .row-large td { background: #f8fafc; color: #94a3b8; } + .row-micro td { + background: #ecfdf5; + } + + .row-small td { + background: #e0f2fe; + } + + .row-medium td { + background: #fffbeb; + } + + .row-large td { + background: #f8fafc; + color: #94a3b8; + } } .msme-dot { @@ -1081,10 +1164,21 @@ form { border-radius: 50%; margin-right: 6px; - &.green { background: #10b981; } - &.blue { background: #0ea5e9; } - &.amber { background: #f59e0b; } - &.gray { background: #cbd5e1; } + &.green { + background: #10b981; + } + + &.blue { + background: #0ea5e9; + } + + &.amber { + background: #f59e0b; + } + + &.gray { + background: #cbd5e1; + } } .msme-class-table-footer { @@ -1201,6 +1295,7 @@ form { .mdc-notched-outline__trailing { border-color: #10b981 !important; } + input:disabled { color: #059669 !important; -webkit-text-fill-color: #059669 !important; @@ -1213,6 +1308,7 @@ form { .mdc-notched-outline__trailing { border-color: #0ea5e9 !important; } + input:disabled { color: #0284c7 !important; -webkit-text-fill-color: #0284c7 !important; @@ -1225,6 +1321,7 @@ form { .mdc-notched-outline__trailing { border-color: #f59e0b !important; } + input:disabled { color: #d97706 !important; -webkit-text-fill-color: #d97706 !important; @@ -1390,7 +1487,7 @@ form { } } -@media (max-width: 768px) { +@media (width <= 768px) { .form-grid { grid-template-columns: 1fr; } @@ -1495,18 +1592,68 @@ form { border-bottom: 1px solid #f1f5f9; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } .sig-table__col { - &--sno { flex: 0 0 32px; text-align: center; color: #94a3b8; font-size: 12px; } - &--photo { flex: 0 0 40px; } - &--name { flex: 1; font-size: 13px; display: flex; align-items: center; gap: 6px; } - &--id { flex: 0 0 100px; font-size: 12px; color: #64748b; } - &--pan { flex: 0 0 120px; font-size: 12px; color: #1e293b; display: flex; align-items: center; gap: 3px; } - &--status { flex: 0 0 80px; } - &--actions { flex: 0 0 80px; display: flex; gap: 2px; } + &--sno { + flex: 0 0 32px; + text-align: center; + color: #94a3b8; + font-size: 12px; + } + + &--photo { + flex: 0 0 40px; + } + + &--name { + flex: 1; + font-size: 13px; + display: flex; + align-items: center; + gap: 6px; + } + + &--id { + flex: 0 0 100px; + font-size: 12px; + color: #64748b; + } + + &--pan { + flex: 0 0 120px; + font-size: 12px; + color: #1e293b; + display: flex; + align-items: center; + gap: 3px; + } + + &--din { + flex: 0 0 140px; + font-size: 12px; + color: #1e293b; + display: flex; + align-items: center; + gap: 3px; + } + + &--status { + flex: 0 0 80px; + } + + &--actions { + flex: 0 0 80px; + display: flex; + gap: 2px; + } } .sig-table__avatar { @@ -1521,11 +1668,21 @@ form { font-size: 12px; overflow: hidden; - &.has-photo { padding: 0; } - .avatar-photo { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; } + &.has-photo { + padding: 0; + } + + .avatar-photo { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; + } } -.sig-inline-spinner { display: inline-block; } +.sig-inline-spinner { + display: inline-block; +} .verify-tick-sm { font-size: 14px !important; @@ -1534,11 +1691,40 @@ form { color: #22c55e; } +.verify-cross-sm { + font-size: 14px !important; + width: 14px !important; + height: 14px !important; + color: #f59e0b; +} + +.din-verify-btn { + font-size: 11px !important; + padding: 2px 8px !important; + min-height: 24px !important; + line-height: 20px !important; + border-radius: 6px !important; + display: flex; + align-items: center; + gap: 4px; + color: #1074b9 !important; + border-color: #93c5fd !important; + + .din-verify-icon { + font-size: 14px !important; + width: 14px !important; + height: 14px !important; + } +} + .sig-status-chip { font-size: 11px; font-weight: 600; color: #94a3b8; - &.active { color: #22c55e; } + + &.active { + color: #22c55e; + } } .sig-action-icon { @@ -1546,7 +1732,10 @@ form { width: 18px !important; height: 18px !important; color: #64748b; - &.remove { color: #ef4444; } + + &.remove { + color: #ef4444; + } } .sig-add-wrapper { @@ -1561,11 +1750,13 @@ form { overflow: visible !important; max-height: none !important; } + .mdc-dialog__surface { border-radius: 12px !important; overflow: visible !important; max-height: none !important; } + .mat-mdc-dialog-content { max-height: none !important; overflow: visible !important; @@ -1625,7 +1816,7 @@ form { &__sub { font-size: 11px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); margin: 2px 0 0; } } diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts index ab92a3241..a091246e0 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts @@ -7,7 +7,17 @@ */ /** Angular Imports */ -import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input, Output, EventEmitter, inject } from '@angular/core'; +import { + Component, + OnInit, + OnDestroy, + OnChanges, + SimpleChanges, + Input, + Output, + EventEmitter, + inject +} from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { @@ -40,6 +50,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { DateFormatPipe } from '../../../pipes/date-format.pipe'; import { SanctionsVerifyDialogComponent } from './sanctions-verify-dialog.component'; import { DipVerifyDialogComponent } from './dip-verify-dialog.component'; +import { DinVerifyDialogComponent } from './din-verify-dialog.component'; /** * Create Client Component @@ -165,17 +176,149 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges alternateDipTimestamp: string | null = null; /** Constitution Types Master with KYC field metadata */ constitutionTypesMaster = [ - { value: 'PRIVATE_LIMITED', label: 'Private Limited Company', icon: 'business', panLabel: 'PAN of Company', panHint: 'PAN mandatory. CIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'Constitution as per Articles of Association', documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', note: 'Board Resolution to specify authorized signatories' }, - { value: 'PUBLIC_LIMITED', label: 'Public Limited Company', icon: 'domain', panLabel: 'PAN of Company', panHint: 'PAN mandatory. CIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'Constitution as per Articles of Association', documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', note: 'Board Resolution to specify authorized signatories' }, - { value: 'LLP', label: 'Limited Liability Partnership (LLP)', icon: 'handshake', panLabel: 'PAN of LLP', panHint: 'PAN mandatory. LLPIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'LLP Agreement', documents: 'Certificate of Incorporation, LLP Agreement, PAN, Designated Partners Resolution', signatoryKyc: 'KYC of all Designated Partners / Authorized Signatories', note: 'LLPIN from MCA required' }, - { value: 'PARTNERSHIP', label: 'Partnership Firm', icon: 'handshake', panLabel: 'PAN of Firm', panHint: 'PAN of Firm mandatory', regHint: 'Registration Certificate No. (if registered)', showCin: false, additionalField: 'Partners Names (linked client-wise)', documents: 'Partnership Deed, Registration Certificate (if registered), PAN of Firm', signatoryKyc: 'KYC of all Partners / Authorized Signatories', note: 'Registration optional but preferred' }, - { value: 'PROPRIETORSHIP', label: 'Proprietorship', icon: 'person', panLabel: 'PAN of Proprietor', panHint: 'PAN of Proprietor (no separate entity PAN needed)', regHint: 'GST / Shop License No.', showCin: false, additionalField: '', documents: 'GST Certificate / Shop License / Utility Bill, PAN of Proprietor', signatoryKyc: "Proprietor's individual KYC (same as individual requirements)", note: 'At least 2 proofs of business existence required. Proprietor name = Client name.' }, - { value: 'HUF', label: 'HUF (Hindu Undivided Family)', icon: 'family_restroom', panLabel: 'PAN of HUF', panHint: 'PAN of HUF mandatory', regHint: 'HUF Declaration / Deed No.', showCin: false, additionalField: 'Karta Name', documents: 'HUF Declaration/Deed, PAN of HUF', signatoryKyc: 'KYC of Karta', note: '' }, - { value: 'TRUST', label: 'Trust', icon: 'verified_user', panLabel: 'PAN of Trust', panHint: 'PAN of Trust mandatory', regHint: 'Trust Deed / Registration No.', showCin: false, additionalField: 'Purpose of Trust', documents: 'Trust Deed, Registration Certificate, PAN of Trust', signatoryKyc: 'KYC of Trustees / Authorized Signatories', note: 'Foreign contribution rules apply if applicable' }, - { value: 'SOCIETY', label: 'Society', icon: 'groups', panLabel: 'PAN of Society', panHint: 'PAN of Society mandatory', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Managing Committee Details', documents: 'Registration Certificate, PAN of Society, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Governed by Societies Registration Act' }, - { value: 'ASSOCIATION', label: 'Association / Club', icon: 'diversity_3', panLabel: 'PAN', panHint: 'PAN mandatory', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Club Constitution Document', documents: 'Registration Certificate, PAN, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Committee approval needed for account' }, - { value: 'GOVT_PSU', label: 'Government Dept / PSU', icon: 'account_balance', panLabel: 'PAN (if applicable)', panHint: 'PAN if applicable', regHint: 'Govt. Notification / Order No.', showCin: false, additionalField: 'Gazette Notification Reference', documents: 'Relevant Government Notification / Order, PAN (if applicable)', signatoryKyc: 'Authorized Signatory KYC', note: 'Special KYC norms as per RBI circular' }, - { value: 'COOPERATIVE', label: 'Cooperative Society / Cooperative Bank', icon: 'hub', panLabel: 'PAN', panHint: 'PAN mandatory. RBI license if bank.', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Bye-laws of the Cooperative', documents: 'Registration Certificate, PAN, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Separate RBI licensing check for cooperative banks' } + { + value: 'PRIVATE_LIMITED', + label: 'Private Limited Company', + icon: 'business', + panLabel: 'PAN of Company', + panHint: 'PAN mandatory. CIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'Constitution as per Articles of Association', + documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', + signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', + note: 'Board Resolution to specify authorized signatories' + }, + { + value: 'PUBLIC_LIMITED', + label: 'Public Limited Company', + icon: 'domain', + panLabel: 'PAN of Company', + panHint: 'PAN mandatory. CIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'Constitution as per Articles of Association', + documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', + signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', + note: 'Board Resolution to specify authorized signatories' + }, + { + value: 'LLP', + label: 'Limited Liability Partnership (LLP)', + icon: 'handshake', + panLabel: 'PAN of LLP', + panHint: 'PAN mandatory. LLPIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'LLP Agreement', + documents: 'Certificate of Incorporation, LLP Agreement, PAN, Designated Partners Resolution', + signatoryKyc: 'KYC of all Designated Partners / Authorized Signatories', + note: 'LLPIN from MCA required' + }, + { + value: 'PARTNERSHIP', + label: 'Partnership Firm', + icon: 'handshake', + panLabel: 'PAN of Firm', + panHint: 'PAN of Firm mandatory', + regHint: 'Registration Certificate No. (if registered)', + showCin: false, + additionalField: 'Partners Names (linked client-wise)', + documents: 'Partnership Deed, Registration Certificate (if registered), PAN of Firm', + signatoryKyc: 'KYC of all Partners / Authorized Signatories', + note: 'Registration optional but preferred' + }, + { + value: 'PROPRIETORSHIP', + label: 'Proprietorship', + icon: 'person', + panLabel: 'PAN of Proprietor', + panHint: 'PAN of Proprietor (no separate entity PAN needed)', + regHint: 'GST / Shop License No.', + showCin: false, + additionalField: '', + documents: 'GST Certificate / Shop License / Utility Bill, PAN of Proprietor', + signatoryKyc: "Proprietor's individual KYC (same as individual requirements)", + note: 'At least 2 proofs of business existence required. Proprietor name = Client name.' + }, + { + value: 'HUF', + label: 'HUF (Hindu Undivided Family)', + icon: 'family_restroom', + panLabel: 'PAN of HUF', + panHint: 'PAN of HUF mandatory', + regHint: 'HUF Declaration / Deed No.', + showCin: false, + additionalField: 'Karta Name', + documents: 'HUF Declaration/Deed, PAN of HUF', + signatoryKyc: 'KYC of Karta', + note: '' + }, + { + value: 'TRUST', + label: 'Trust', + icon: 'verified_user', + panLabel: 'PAN of Trust', + panHint: 'PAN of Trust mandatory', + regHint: 'Trust Deed / Registration No.', + showCin: false, + additionalField: 'Purpose of Trust', + documents: 'Trust Deed, Registration Certificate, PAN of Trust', + signatoryKyc: 'KYC of Trustees / Authorized Signatories', + note: 'Foreign contribution rules apply if applicable' + }, + { + value: 'SOCIETY', + label: 'Society', + icon: 'groups', + panLabel: 'PAN of Society', + panHint: 'PAN of Society mandatory', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Managing Committee Details', + documents: 'Registration Certificate, PAN of Society, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Governed by Societies Registration Act' + }, + { + value: 'ASSOCIATION', + label: 'Association / Club', + icon: 'diversity_3', + panLabel: 'PAN', + panHint: 'PAN mandatory', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Club Constitution Document', + documents: 'Registration Certificate, PAN, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Committee approval needed for account' + }, + { + value: 'GOVT_PSU', + label: 'Government Dept / PSU', + icon: 'account_balance', + panLabel: 'PAN (if applicable)', + panHint: 'PAN if applicable', + regHint: 'Govt. Notification / Order No.', + showCin: false, + additionalField: 'Gazette Notification Reference', + documents: 'Relevant Government Notification / Order, PAN (if applicable)', + signatoryKyc: 'Authorized Signatory KYC', + note: 'Special KYC norms as per RBI circular' + }, + { + value: 'COOPERATIVE', + label: 'Cooperative Society / Cooperative Bank', + icon: 'hub', + panLabel: 'PAN', + panHint: 'PAN mandatory. RBI license if bank.', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Bye-laws of the Cooperative', + documents: 'Registration Certificate, PAN, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Separate RBI licensing check for cooperative banks' + } ]; /** Currently selected constitution metadata */ @@ -205,18 +348,18 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.maxDate = this.settingsService.businessDate; this.setOptions(); // Fetch marital status options from code values - this.clientService.getCodeValuesByName('MaritalStatus').subscribe(options => { + this.clientService.getCodeValuesByName('MaritalStatus').subscribe((options) => { this.maritalStatusOptions = options || []; }); // Fetch all constitution options from code ID 17 - this.clientService.getCodeValuesById(17).subscribe(options => { + this.clientService.getCodeValuesById(17).subscribe((options) => { if (options?.length) { this.constitutionOptions = options; // Sort master by value length descending so longer/more-specific keys match first const sortedMaster = [...this.constitutionTypesMaster].sort((a, b) => b.value.length - a.value.length); this.enrichedConstitutionOptions = options.map((opt: any) => { const normalized = opt.name?.toUpperCase().replace(/[\s_-]+/g, '') || ''; - const master = sortedMaster.find(m => normalized.includes(m.value.replace(/_/g, ''))); + const master = sortedMaster.find((m) => normalized.includes(m.value.replace(/_/g, ''))); return { id: opt.id, value: master?.value || opt.name, @@ -263,7 +406,9 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges occupationId: data.occupation?.id || data.occupationId || '', monthlyIncome: data.monthlyIncome || '', annualTurnover: data.annualTurnover || '', - submittedOnDate: data.timeline?.submittedOnDate ? new Date(data.timeline.submittedOnDate) : this.settingsService.businessDate + submittedOnDate: data.timeline?.submittedOnDate + ? new Date(data.timeline.submittedOnDate) + : this.settingsService.businessDate }); // Patch name fields (already created by buildDependencies via legalFormId subscription) @@ -323,19 +468,55 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges active: [false], addSavings: [false], accountNo: [''], - genderId: ['', Validators.required], + genderId: [ + '', + Validators.required + ], maritalStatusId: [''], - mobileNo: ['', [Validators.required, Validators.pattern(/^[6-9]\d{9}$/), Validators.maxLength(10)]], - alternateMobileNo: ['', [Validators.pattern(/^[6-9]\d{9}$/), Validators.maxLength(10)]], + mobileNo: [ + '', + [ + Validators.required, + Validators.pattern(/^[6-9]\d{9}$/), + Validators.maxLength(10) + ] + ], + alternateMobileNo: [ + '', + [ + Validators.pattern(/^[6-9]\d{9}$/), + Validators.maxLength(10) + ] + ], emailAddress: [ '', - [Validators.required, Validators.email, Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), Validators.maxLength(100)] + [ + Validators.required, + Validators.email, + Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), + Validators.maxLength(100) + ] + ], + dateOfBirth: [ + '', + Validators.required ], - dateOfBirth: ['', Validators.required], occupationCategoryId: [''], occupationId: [''], - monthlyIncome: ['', [Validators.pattern(/^\d+(\.\d{1,2})?$/), Validators.min(0)]], - annualTurnover: ['', [Validators.pattern(/^\d+(\.\d{1,2})?$/), Validators.min(0)]], + monthlyIncome: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], + annualTurnover: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], submittedOnDate: [ this.settingsService.businessDate, Validators.required @@ -387,7 +568,13 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges Validators.maxLength(50) ]) ); - this.createClientForm.addControl('middlename', new UntypedFormControl('', [Validators.pattern(/^[A-Za-z][A-Za-z\s.'-]*$/), Validators.maxLength(50)])); + this.createClientForm.addControl( + 'middlename', + new UntypedFormControl('', [ + Validators.pattern(/^[A-Za-z][A-Za-z\s.'-]*$/), + Validators.maxLength(50) + ]) + ); this.createClientForm.addControl( 'lastname', new UntypedFormControl('', [ @@ -417,7 +604,10 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.createClientForm.addControl( 'clientNonPersonDetails', this.formBuilder.group({ - constitutionId: ['', Validators.required], + constitutionId: [ + '', + Validators.required + ], mainBusinessLineId: [''], incorpNumber: [''], authorizedSignatory: [''], @@ -466,10 +656,19 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.isMinor = years < 10; // Add/remove guardian form group based on minor status if (this.isMinor && !wasMinor) { - this.createClientForm.addControl('guardianDetails', this.formBuilder.group({ - guardianClientId: ['', Validators.required], - guardianRelation: ['', Validators.required] - })); + this.createClientForm.addControl( + 'guardianDetails', + this.formBuilder.group({ + guardianClientId: [ + '', + Validators.required + ], + guardianRelation: [ + '', + Validators.required + ] + }) + ); this.selectedGuardian = null; this.guardianSearchText = ''; this.guardianSearchResults = []; @@ -522,13 +721,15 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges }); // Reset DIP verification when mobile number changes - this.createClientForm.get('mobileNo') + this.createClientForm + .get('mobileNo') .valueChanges.pipe(takeUntil(this.destroy$)) .subscribe(() => { this.mobileVerified = false; }); - this.createClientForm.get('alternateMobileNo') + this.createClientForm + .get('alternateMobileNo') .valueChanges.pipe(takeUntil(this.destroy$)) .subscribe(() => { this.alternateMobileVerified = false; @@ -699,7 +900,9 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges target.officeName = data.officeName || null; target._loading = false; }, - error: () => { target._loading = false; } + error: () => { + target._loading = false; + } }); this.clientService.getClientKyc(cid).subscribe({ @@ -709,24 +912,36 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges target.aadhaarMasked = kyc?.aadhaarNumberMasked || '-'; target.aadhaarVerified = kyc?.aadhaarVerified || false; }, - error: () => { target.panNumber = '-'; target.aadhaarMasked = '-'; } + error: () => { + target.panNumber = '-'; + target.aadhaarMasked = '-'; + } }); this.clientService.getClientAccountData(cid).subscribe({ next: (accts: any) => { const savings = accts?.savingsAccounts || []; const active = savings.find((a: any) => a.status?.active) || savings[0]; - target.accountType = active ? (active.productName || 'Savings') : '-'; + target.accountType = active ? active.productName || 'Savings' : '-'; target.accountNo = active?.accountNo || '-'; }, - error: () => { target.accountType = '-'; target.accountNo = '-'; } + error: () => { + target.accountType = '-'; + target.accountNo = '-'; + } }); this.clientService.getClientAddressData(cid).subscribe({ next: (addresses: any[]) => { if (addresses?.length) { const addr = addresses[0]; - const parts = [addr.addressLine1, addr.townVillage, addr.city, addr.countyDistrict, addr.addressLine3].filter(Boolean); + const parts = [ + addr.addressLine1, + addr.townVillage, + addr.city, + addr.countyDistrict, + addr.addressLine3 + ].filter(Boolean); if (parts.length) { target.fullAddress = parts.join(', ') + (addr.postalCode ? ' - ' + addr.postalCode : ''); } @@ -748,8 +963,8 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges // Fetch signature image this.clientService.getClientDocuments(cid).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => - d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') ); if (sigDoc) { this.clientService.getClientSignatureImage(cid, sigDoc.id.toString()).subscribe({ @@ -800,19 +1015,29 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges onSignatorySearch(text: string): void { this.signatorySearchText = text; clearTimeout(this.signatorySearchTimer); - if (!text || text.length < 2) { this.signatorySearchResults = []; return; } + if (!text || text.length < 2) { + this.signatorySearchResults = []; + return; + } this.signatorySearching = true; this.signatorySearchTimer = setTimeout(() => { this.clientService.searchByText(text, 0, 10).subscribe({ - next: (data: any) => { this.signatorySearchResults = data?.content || []; this.signatorySearching = false; this.clientService.enrichSearchResults(this.signatorySearchResults); }, - error: () => { this.signatorySearchResults = []; this.signatorySearching = false; } + next: (data: any) => { + this.signatorySearchResults = data?.content || []; + this.signatorySearching = false; + this.clientService.enrichSearchResults(this.signatorySearchResults); + }, + error: () => { + this.signatorySearchResults = []; + this.signatorySearching = false; + } }); }, 400); } addSignatory(client: any): void { // Prevent duplicates - if (this.signatories.find(s => s.id === client.id)) { + if (this.signatories.find((s) => s.id === client.id)) { this.signatorySearchResults = []; this.signatorySearchText = ''; return; @@ -824,6 +1049,49 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.syncSignatoryNames(); this.fetchClientFullDetails(client.id, sig); + this.openDinVerifyDialog(sig); + } + + /** Open DIN verification dialog for a signatory */ + openDinVerifyDialog(sig: any): void { + const cid = sig.id?.toString(); + if (!cid) return; + + const entityName = this.createClientForm.get('fullname')?.value || ''; + + const openDialog = (kycId: string, panFromKyc?: string) => { + const ref = this.dialog.open(DinVerifyDialogComponent, { + width: '620px', + maxWidth: '95vw', + disableClose: false, + autoFocus: true, + data: { + clientId: cid, + kycId, + signatoryName: sig.displayName || '', + cifNumber: sig.cifNumber || sig.accountNumber || cid, + panNumber: sig.panNumber || panFromKyc || '-', + entityName + } + }); + ref.afterClosed().subscribe((result: any) => { + if (result && !result.skipped) { + sig.dinNumber = result.dinNumber || ''; + sig.dinVerified = result.dinVerified || false; + sig.directorName = result.directorName || ''; + sig.dinStatus = result.dinStatus || ''; + sig.dinDesignation = result.dinDesignation || ''; + sig.companyMatch = result.companyMatch ?? null; + sig.matchedCompany = result.matchedCompany || ''; + } + }); + }; + + // Fetch KYC ID first, then open dialog + this.clientService.getClientKyc(cid).subscribe({ + next: (kyc: any) => openDialog(kyc?.id?.toString() || '', kyc?.panNumber), + error: () => openDialog('') + }); } removeSignatoryAt(index: number): void { @@ -842,26 +1110,26 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges } private syncSignatoryNames(): void { - const names = this.signatories.map(s => s.displayName).join(', '); + const names = this.signatories.map((s) => s.displayName).join(', '); this.createClientForm.get('clientNonPersonDetails')?.patchValue({ authorizedSignatory: names }); } /** Watch MSME turnover & investment for auto-classification */ /** Maps salutation name to gender. Returns gender option or null. */ private salutationGenderMap: Record = { - 'mr': 'male', + mr: 'male', 'mr.': 'male', - 'shri': 'male', - 'sri': 'male', - 'master': 'male', - 'mrs': 'female', + shri: 'male', + sri: 'male', + master: 'male', + mrs: 'female', 'mrs.': 'female', - 'ms': 'female', + ms: 'female', 'ms.': 'female', - 'miss': 'female', - 'smt': 'female', + miss: 'female', + smt: 'female', 'smt.': 'female', - 'kumari': 'female', + kumari: 'female' // Dr, Er, CA, Adv etc. are gender-neutral — no auto-fill }; @@ -875,39 +1143,41 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges const salCtrl = this.createClientForm.get('salutationId'); if (!salCtrl) return; - this.salutationSub = salCtrl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe((salutationId: any) => { - if (!salutationId || !this.salutationOptions?.length || !this.genderOptions?.length) return; + this.salutationSub = salCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((salutationId: any) => { + if (!salutationId || !this.salutationOptions?.length || !this.genderOptions?.length) return; - // Find the salutation name from the selected ID - const salutation = this.salutationOptions.find((s: any) => s.id === salutationId); - if (!salutation) return; + // Find the salutation name from the selected ID + const salutation = this.salutationOptions.find((s: any) => s.id === salutationId); + if (!salutation) return; - const salName = (salutation.name || '').toLowerCase().trim().replace(/\.$/, ''); - const mappedGender = this.salutationGenderMap[salName] || this.salutationGenderMap[salName + '.']; - if (!mappedGender) return; + const salName = (salutation.name || '').toLowerCase().trim().replace(/\.$/, ''); + const mappedGender = this.salutationGenderMap[salName] || this.salutationGenderMap[salName + '.']; + if (!mappedGender) return; - // Find matching gender option by name - const genderOption = this.genderOptions.find((g: any) => - (g.name || '').toLowerCase().trim() === mappedGender - ); + // Find matching gender option by name + const genderOption = this.genderOptions.find((g: any) => (g.name || '').toLowerCase().trim() === mappedGender); - if (genderOption) { - this.createClientForm.get('genderId')?.setValue(genderOption.id); - } - }); + if (genderOption) { + this.createClientForm.get('genderId')?.setValue(genderOption.id); + } + }); } /** Returns capitalized full name preview from first + middle + last */ get fullNamePreview(): string { - const capitalize = (s: string) => s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : ''; + const capitalize = (s: string) => (s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : ''); const salutationId = this.createClientForm?.get('salutationId')?.value; const salutation = this.salutationOptions?.find((s: any) => s.id === salutationId)?.name || ''; const first = capitalize(this.createClientForm?.get('firstname')?.value || ''); const middle = capitalize(this.createClientForm?.get('middlename')?.value || ''); const last = capitalize(this.createClientForm?.get('lastname')?.value || ''); - const full = [first, middle, last].filter(Boolean).join(' '); + const full = [ + first, + middle, + last + ] + .filter(Boolean) + .join(' '); return salutation ? `${salutation} ${full}` : full; } @@ -985,7 +1255,7 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges getSelectedNationality(): any { const val = this.createClientForm.get('nationalityId')?.value; - return this.nationalityOptions.find(n => n.value === val) || null; + return this.nationalityOptions.find((n) => n.value === val) || null; } getSelectedBusiness(): any { @@ -1111,14 +1381,17 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges } const unsupportedFields = [ - 'guardianDetails', 'addressDetails', 'entityKycDetails' + 'guardianDetails', + 'addressDetails', + 'entityKycDetails' ]; - unsupportedFields.forEach(f => delete generalDetails[f]); + unsupportedFields.forEach((f) => delete generalDetails[f]); // Format dateOfRegistration if present if (generalDetails.clientNonPersonDetails?.dateOfRegistration instanceof Date) { generalDetails.clientNonPersonDetails.dateOfRegistration = this.dateUtils.formatDate( - generalDetails.clientNonPersonDetails.dateOfRegistration, dateFormat + generalDetails.clientNonPersonDetails.dateOfRegistration, + dateFormat ); } @@ -1198,14 +1471,18 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges credit_card PAN {{ data.panNumber || '-' }} - @if (data.panVerified) { check_circle } + @if (data.panVerified) { + check_circle + }
fingerprint Aadhaar {{ data.aadhaarMasked || '-' }} - @if (data.aadhaarVerified) { check_circle } + @if (data.aadhaarVerified) { + check_circle + }
@@ -1243,73 +1520,186 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
`, - styles: [` - :host { --bg: #F0F4F8; --surface: #FFFFFF; --border: #E2E8F0; --border-input: #CBD5E1; - --primary: #1E6BB8; --primary-light: #EBF3FF; --text: #1A2332; --text-muted: #64748B; - --radius-card: 12px; } + styles: [ + ` + :host { + --bg: #f0f4f8; + --surface: #ffffff; + --border: #e2e8f0; + --border-input: #cbd5e1; + --primary: #1e6bb8; + --primary-light: #ebf3ff; + --text: #1a2332; + --text-muted: #64748b; + --radius-card: 12px; + } - .sd { background: var(--bg); border-radius: var(--radius-card); overflow: hidden; } + .sd { + background: var(--bg); + border-radius: var(--radius-card); + overflow: hidden; + } - /* Header */ - .sd-header { - display: flex; align-items: center; gap: 14px; - padding: 20px 24px; background: var(--surface); border-bottom: 1px solid var(--border); - } - .sd-avatar { - width: 56px; height: 56px; border-radius: 50%; background: var(--primary-light); - display: flex; align-items: center; justify-content: center; - flex-shrink: 0; overflow: hidden; border: 2px solid var(--border); - } - .sd-avatar--photo { border-color: var(--primary); } - .sd-avatar img { width: 100%; height: 100%; object-fit: cover; } - .sd-avatar-icon { font-size: 22px; color: var(--primary); } - .sd-header-info { flex: 1; } - .sd-name { margin: 0; font-size: 18px; font-weight: 700; color: var(--text); } - .sd-sub { margin: 2px 0 0; font-size: 13px; color: var(--text-muted); } - .sd-close { color: var(--text-muted); } + /* Header */ + .sd-header { + display: flex; + align-items: center; + gap: 14px; + padding: 20px 24px; + background: var(--surface); + border-bottom: 1px solid var(--border); + } + .sd-avatar { + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--primary-light); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + overflow: hidden; + border: 2px solid var(--border); + } + .sd-avatar--photo { + border-color: var(--primary); + } + .sd-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + .sd-avatar-icon { + font-size: 22px; + color: var(--primary); + } + .sd-header-info { + flex: 1; + } + .sd-name { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--text); + } + .sd-sub { + margin: 2px 0 0; + font-size: 13px; + color: var(--text-muted); + } + .sd-close { + color: var(--text-muted); + } - /* Body */ - .sd-body { padding: 0 !important; background: var(--bg); max-height: none !important; overflow: visible !important; padding-bottom: 16px !important; } - .sd-loading { display: flex; align-items: center; gap: 10px; padding: 20px 24px; color: var(--text-muted); font-size: 13px; } + /* Body */ + .sd-body { + padding: 0 !important; + background: var(--bg); + max-height: none !important; + overflow: visible !important; + padding-bottom: 16px !important; + } + .sd-loading { + display: flex; + align-items: center; + gap: 10px; + padding: 20px 24px; + color: var(--text-muted); + font-size: 13px; + } - /* Sections */ - .sd-section { - background: var(--surface); margin: 12px 16px; padding: 16px 20px; - border-radius: var(--radius-card); border: 1px solid var(--border); - } - .sd-section--kyc { background: var(--primary-light); border-color: #c7ddf5; } - .sd-section--sig { padding-bottom: 20px; } + /* Sections */ + .sd-section { + background: var(--surface); + margin: 12px 16px; + padding: 16px 20px; + border-radius: var(--radius-card); + border: 1px solid var(--border); + } + .sd-section--kyc { + background: var(--primary-light); + border-color: #c7ddf5; + } + .sd-section--sig { + padding-bottom: 20px; + } - .sd-row { display: flex; gap: 24px; } - .sd-row + .sd-row { margin-top: 14px; } - .sd-field { flex: 1; display: flex; flex-direction: column; } - .sd-field--full { flex: 1 1 100%; } + .sd-row { + display: flex; + gap: 24px; + } + .sd-row + .sd-row { + margin-top: 14px; + } + .sd-field { + flex: 1; + display: flex; + flex-direction: column; + } + .sd-field--full { + flex: 1 1 100%; + } - .sd-label { - font-size: 11px; font-weight: 600; text-transform: uppercase; color: var(--text-muted); - letter-spacing: 0.6px; display: flex; align-items: center; gap: 4px; margin-bottom: 4px; - } - .sd-val { - font-size: 15px; font-weight: 600; color: var(--text); - display: flex; align-items: center; gap: 5px; - } - .sd-val--mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; letter-spacing: 0.5px; } + .sd-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + color: var(--text-muted); + letter-spacing: 0.6px; + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 4px; + } + .sd-val { + font-size: 15px; + font-weight: 600; + color: var(--text); + display: flex; + align-items: center; + gap: 5px; + } + .sd-val--mono { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + letter-spacing: 0.5px; + } - .sd-icon { font-size: 15px !important; width: 15px !important; height: 15px !important; color: var(--primary); } - .sd-tick { font-size: 18px !important; width: 18px !important; height: 18px !important; color: var(--primary); } + .sd-icon { + font-size: 15px !important; + width: 15px !important; + height: 15px !important; + color: var(--primary); + } + .sd-tick { + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + color: var(--primary); + } - /* Signature frame */ - .sd-sig-frame { - margin-top: 8px; padding: 12px; background: var(--surface); - border: 1px dashed var(--border-input); border-radius: 8px; - display: inline-block; - } - .sd-sig-frame img { max-width: 300px; max-height: 100px; display: block; } + /* Signature frame */ + .sd-sig-frame { + margin-top: 8px; + padding: 12px; + background: var(--surface); + border: 1px dashed var(--border-input); + border-radius: 8px; + display: inline-block; + } + .sd-sig-frame img { + max-width: 300px; + max-height: 100px; + display: block; + } - @media (max-width: 480px) { - .sd-row { flex-direction: column; gap: 12px; } - } - `], + @media (max-width: 480px) { + .sd-row { + flex-direction: column; + gap: 12px; + } + } + ` + ], imports: [ STANDALONE_SHARED_IMPORTS, MatIcon, @@ -1322,10 +1712,12 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges export class SignatoryDetailDialogComponent { private clientService = inject(ClientsService); private sanitizer = inject(DomSanitizer); + private inputData = inject(MAT_DIALOG_DATA); data: any; loading = false; - constructor(@Inject(MAT_DIALOG_DATA) inputData: any) { + constructor() { + const inputData = this.inputData; this.data = { ...inputData }; const cid = inputData.id?.toString(); if (!cid) return; @@ -1333,18 +1725,25 @@ export class SignatoryDetailDialogComponent { // Fetch profile image this.clientService.getClientProfileImage(cid).subscribe({ next: (img: any) => { - if (img) { this.data = { ...this.data, profileImageUrl: this.sanitizer.bypassSecurityTrustResourceUrl(img) }; } + if (img) { + this.data = { ...this.data, profileImageUrl: this.sanitizer.bypassSecurityTrustResourceUrl(img) }; + } } }); // Fetch signature this.clientService.getClientDocuments(cid).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature')); + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + ); if (sigDoc) { this.clientService.getClientSignatureImage(cid, sigDoc.id.toString()).subscribe({ next: (blob: Blob) => { - this.data = { ...this.data, signatureUrl: this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)) }; + this.data = { + ...this.data, + signatureUrl: this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)) + }; } }); } @@ -1371,19 +1770,54 @@ export class SignatoryDetailDialogComponent {
`, - styles: [` - .zoom-dialog { background: #fff; border-radius: 12px; overflow: hidden; } - .zoom-dialog__header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: #0f172a; color: #fff; } - .zoom-dialog__title { font-size: 14px; font-weight: 600; } - .zoom-dialog__close { color: rgba(255,255,255,0.7) !important; } - .zoom-dialog__close:hover { color: #fff !important; } - .zoom-dialog__body { padding: 20px; display: flex; align-items: center; justify-content: center; background: #f8fafc; } - .zoom-dialog__img { max-width: 100%; max-height: 70vh; object-fit: contain; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } - `], - imports: [MatIcon, MatDialogClose] + styles: [ + ` + .zoom-dialog { + background: #fff; + border-radius: 12px; + overflow: hidden; + } + .zoom-dialog__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: #0f172a; + color: #fff; + } + .zoom-dialog__title { + font-size: 14px; + font-weight: 600; + } + .zoom-dialog__close { + color: rgba(255, 255, 255, 0.7) !important; + } + .zoom-dialog__close:hover { + color: #fff !important; + } + .zoom-dialog__body { + padding: 20px; + display: flex; + align-items: center; + justify-content: center; + background: #f8fafc; + } + .zoom-dialog__img { + max-width: 100%; + max-height: 70vh; + object-fit: contain; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + } + ` + ], + imports: [ + MatIcon, + MatDialogClose + ] }) export class GuardianImageZoomDialogComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: { imageUrl: string; title: string }) {} + data = inject<{ imageUrl: string; title: string }>(MAT_DIALOG_DATA); } /** @@ -1409,17 +1843,59 @@ export class GuardianImageZoomDialogComponent {
`, - styles: [` - .gdc { background: #fff; border-radius: 12px; overflow: hidden; } - .gdc-header { display: flex; align-items: center; gap: 10px; padding: 16px 20px; background: #fef2f2; border-bottom: 1px solid #fecaca; } - .gdc-header__icon { color: #dc2626; font-size: 24px; width: 24px; height: 24px; } - .gdc-header__title { margin: 0; font-size: 16px; font-weight: 600; color: #991b1b; } - .gdc-body { padding: 20px; } - .gdc-body p { margin: 0; font-size: 14px; color: #374151; line-height: 1.5; } - .gdc-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 20px; border-top: 1px solid #f1f5f9; } - .gdc-actions button { display: flex; align-items: center; gap: 6px; } - `], - imports: [MatIcon, MatDialogClose] + styles: [ + ` + .gdc { + background: #fff; + border-radius: 12px; + overflow: hidden; + } + .gdc-header { + display: flex; + align-items: center; + gap: 10px; + padding: 16px 20px; + background: #fef2f2; + border-bottom: 1px solid #fecaca; + } + .gdc-header__icon { + color: #dc2626; + font-size: 24px; + width: 24px; + height: 24px; + } + .gdc-header__title { + margin: 0; + font-size: 16px; + font-weight: 600; + color: #991b1b; + } + .gdc-body { + padding: 20px; + } + .gdc-body p { + margin: 0; + font-size: 14px; + color: #374151; + line-height: 1.5; + } + .gdc-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + padding: 12px 20px; + border-top: 1px solid #f1f5f9; + } + .gdc-actions button { + display: flex; + align-items: center; + gap: 6px; + } + ` + ], + imports: [ + MatIcon, + MatDialogClose + ] }) export class GuardianDeleteConfirmDialogComponent {} - diff --git a/src/app/clients/client-stepper/client-general-step/din-verify-dialog.component.ts b/src/app/clients/client-stepper/client-general-step/din-verify-dialog.component.ts new file mode 100644 index 000000000..d27edb9ab --- /dev/null +++ b/src/app/clients/client-stepper/client-general-step/din-verify-dialog.component.ts @@ -0,0 +1,922 @@ +/** + * 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, OnDestroy, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatIconModule } from '@angular/material/icon'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ClientsService } from 'app/clients/clients.service'; + +@Component({ + selector: 'mifosx-din-verify-dialog', + template: ` +

+ + DIN Verification +

+ + + @if (!verifying && !verified && !failed && !errorOccurred) { +
+
+ +
+ {{ signatoryName }} + CIF: {{ cifNumber }} · PAN: {{ panNumber }} +
+
+ @if (entityName) { +
+ +
+ Entity + {{ entityName }} +
+
+ } +
+ + Enter the Director Identification Number (DIN) issued by the Ministry of Corporate + Affairs (MCA) for this authorized signatory. The system will verify the DIN and match the entity name + against the director's associated companies. +
+ + DIN Number + badge + + 8-digit DIN number from MCA + +
+ } + + + @if (verifying) { +
+ +
{{ progress }}%
+
+ @for (step of progressSteps; track step.label; let i = $index) { +
+ + {{ step.label }} +
+ } +
+

+ Verifying DIN {{ dinNumber }} for {{ signatoryName }} against + MCA (Ministry of Corporate Affairs) records. +

+
+ } + + + @if (!verifying && verified) { +
+
+ +
+
VERIFIED
+

DIN Verified Successfully

+

+ DIN {{ dinNumber }} for {{ signatoryName }} has been + verified against MCA records. +

+
+
+ DIN + {{ dinNumber }} +
+ @if (directorName) { +
+ Director Name + {{ directorName }} +
+ } +
+ Status + {{ dinStatus || 'Active' }} +
+
+ Verified On + {{ verifiedDate }} +
+ @if (dinDesignation) { +
+ Designation + {{ dinDesignation }} +
+ } +
+ + + @if (companies.length > 0) { +
+

+ + Associated Companies ({{ companies.length }}) +

+ + @if (companyMatch) { +
+ +
+ Entity Match Found +

+ {{ entityName }} matches {{ matchedCompany }} +

+
+
+ } @else { +
+ +
+ No Entity Match +

+ {{ entityName }} does not match any of the director's associated companies. +

+
+
+ } + +
+ @for (company of companies; track company.companyName || $index) { +
+ @if (company._matched) { + check_circle + } @else { + radio_button_unchecked + } +
+ {{ company.companyName }} + + @if (company.cin) { + CIN: {{ company.cin }} + } + @if (company.cin && company.designation) { + · + } + @if (company.designation) { + {{ company.designation }} + } + @if ((company.cin || company.designation) && company.appointmentDate) { + · + } + @if (company.appointmentDate) { + Since: {{ company.appointmentDate }} + } + +
+
+ } +
+
+ } +
+ } + + + @if (!verifying && failed) { +
+
+ +
+
FAILED
+

DIN Verification Failed

+

+ DIN {{ dinNumber }} could not be verified. +

+ @if (failReason) { +
+ + {{ failReason }} +
+ } +
+ } + + + @if (!verifying && errorOccurred) { +
+ +

Verification failed. Please try again.

+

{{ errorMessage }}

+
+ } +
+ + + @if (!verifying && !verified && !failed && !errorOccurred) { + + + } + @if (!verifying && verified) { + + } + @if (!verifying && failed) { + + + } + @if (!verifying && errorOccurred) { + + + } + + `, + styles: [ + ` + :host { + display: block; + min-width: 520px; + } + + h2[mat-dialog-title] { + display: flex; + align-items: center; + gap: 8px; + margin: 0; + padding: 16px 24px; + font-size: 18px; + font-weight: 600; + color: #1074b9; + } + + .dialog-title-icon { + font-size: 20px; + } + + mat-dialog-content { + padding: 16px 24px; + max-height: 70vh; + overflow: auto; + } + + /* Signatory info */ + .signatory-info { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + background: #f0f4f8; + border-radius: 10px; + margin-bottom: 10px; + border: 1px solid #e2e8f0; + } + + .sig-icon { + font-size: 22px; + color: #1074b9; + } + .sig-detail { + display: flex; + flex-direction: column; + } + .sig-name { + font-size: 15px; + font-weight: 700; + color: #1a2332; + } + .sig-meta { + font-size: 12px; + color: #64748b; + margin-top: 2px; + } + + /* Entity info */ + .entity-info { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 16px; + background: #fffbeb; + border-radius: 8px; + margin-bottom: 14px; + border: 1px solid #fde68a; + } + + .entity-icon { + font-size: 18px; + color: #b45309; + } + .entity-detail { + display: flex; + flex-direction: column; + } + .entity-label { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: #92400e; + } + .entity-name { + font-size: 14px; + font-weight: 700; + color: #78350f; + } + + /* DIN info bar */ + .din-info-bar { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 10px 14px; + background: #e3f2fd; + border-radius: 8px; + margin-bottom: 16px; + font-size: 13px; + color: #333; + line-height: 1.5; + } + + .info-icon { + font-size: 16px; + color: #1074b9; + flex-shrink: 0; + margin-top: 2px; + } + + /* DIN input */ + .din-input-field { + width: 100%; + } + .din-prefix-icon { + color: #64748b; + margin-right: 4px; + } + + /* Progress */ + .progress-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + } + .progress-percent { + font-size: 22px; + font-weight: 700; + color: #1074b9; + margin-top: 4px; + } + + .progress-steps { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; + padding: 8px 16px; + } + .progress-step { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: #9ca3af; + transition: color 0.3s ease; + } + .progress-step.active { + color: #1074b9; + font-weight: 600; + } + .progress-step.done { + color: #388e3c; + } + .done-icon { + color: #388e3c; + } + + .verify-msg { + font-size: 14px; + color: #555; + line-height: 1.6; + text-align: center; + margin: 4px 0 0; + } + + /* Result cards */ + .result-card { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px 16px; + gap: 12px; + } + .result-icon-ring { + width: 64px; + height: 64px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + } + .success-ring { + background: linear-gradient(135deg, #ecfdf5, #d1fae5); + border: 3px solid #a7f3d0; + } + .fail-ring { + background: linear-gradient(135deg, #fef2f2, #fecaca); + border: 3px solid #fca5a5; + } + .result-shield { + font-size: 28px; + } + .success-shield { + color: #059669; + } + .fail-shield { + color: #dc2626; + } + + .result-badge { + display: inline-block; + font-size: 11px; + font-weight: 800; + letter-spacing: 1.5px; + padding: 3px 14px; + border-radius: 20px; + } + .success-badge { + color: #059669; + background: #ecfdf5; + border: 1px solid #a7f3d0; + } + .fail-badge { + color: #dc2626; + background: #fef2f2; + border: 1px solid #fecaca; + } + + .result-title { + font-size: 17px; + font-weight: 700; + color: #111827; + margin: 0; + } + .result-detail { + font-size: 13px; + color: #6b7280; + text-align: center; + line-height: 1.6; + margin: 0; + max-width: 400px; + } + .din-highlight { + color: #059669; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + } + .din-highlight-fail { + color: #dc2626; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + } + + .result-meta { + width: 100%; + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 8px; + padding: 12px 16px; + margin-top: 4px; + } + .meta-row { + display: flex; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid #f3f4f6; + } + .meta-row:last-child { + border-bottom: none; + } + .meta-label { + font-size: 12px; + color: #9ca3af; + font-weight: 500; + } + .meta-value { + font-size: 12px; + color: #374151; + font-weight: 600; + } + .meta-mono { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + letter-spacing: 0.5px; + } + .meta-success { + color: #059669; + } + + /* ═══ Company Match Section ═══ */ + .company-match-section { + width: 100%; + margin-top: 8px; + } + + .company-match-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 700; + color: #1e293b; + margin: 0 0 10px; + } + + .company-title-icon { + font-size: 14px; + color: #64748b; + } + + /* Match banner */ + .match-banner { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 12px 14px; + border-radius: 8px; + margin-bottom: 10px; + font-size: 13px; + line-height: 1.5; + } + + .match-banner p { + margin: 2px 0 0; + font-size: 12px; + } + + .match-success { + background: #ecfdf5; + border: 1px solid #a7f3d0; + color: #065f46; + } + + .match-success .match-banner-icon { + color: #059669; + font-size: 18px; + margin-top: 1px; + flex-shrink: 0; + } + + .match-warning { + background: #fffbeb; + border: 1px solid #fde68a; + color: #92400e; + } + + .match-warning .match-banner-icon { + color: #d97706; + font-size: 18px; + margin-top: 1px; + flex-shrink: 0; + } + + /* Company list */ + .company-list { + border: 1px solid #e5e7eb; + border-radius: 8px; + overflow: hidden; + } + + .company-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 14px; + border-bottom: 1px solid #f3f4f6; + transition: background 0.15s; + } + + .company-item:last-child { + border-bottom: none; + } + + .company-item:hover { + background: #f8fafc; + } + + .company-matched { + background: #f0fdf4 !important; + border-left: 3px solid #22c55e; + } + + .company-match-icon { + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + flex-shrink: 0; + } + + .company-match-icon.match { + color: #22c55e; + } + .company-match-icon.no-match { + color: #d1d5db; + } + + .company-info { + display: flex; + flex-direction: column; + min-width: 0; + } + .company-name { + font-size: 13px; + font-weight: 600; + color: #1e293b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .company-meta { + font-size: 11px; + color: #94a3b8; + margin-top: 2px; + } + + /* Fail reason */ + .fail-reason { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + background: #fef2f2; + border: 1px solid #fecaca; + padding: 10px 14px; + border-radius: 6px; + font-size: 13px; + color: #991b1b; + line-height: 1.5; + } + .fail-reason-icon { + font-size: 16px; + color: #dc2626; + flex-shrink: 0; + } + + /* Error */ + .error-icon-large { + font-size: 48px; + color: #f57c00; + } + .error-msg { + font-size: 16px; + font-weight: 600; + color: #f57c00; + margin: 0; + text-align: center; + } + .error-detail { + font-size: 14px; + color: #555; + text-align: center; + } + + mat-dialog-actions { + padding: 8px 24px 16px; + } + mat-dialog-actions button { + display: flex; + align-items: center; + gap: 6px; + } + ` + ], + imports: [ + CommonModule, + FormsModule, + MatDialogModule, + MatProgressBarModule, + MatButtonModule, + MatFormFieldModule, + MatInputModule, + MatIconModule, + FaIconComponent + ] +}) +export class DinVerifyDialogComponent implements OnInit, OnDestroy { + private dialogRef = inject(MatDialogRef); + private data: { + clientId: string; + kycId: string; + signatoryName: string; + cifNumber: string; + panNumber: string; + entityName: string; + } = inject(MAT_DIALOG_DATA); + private clientsService = inject(ClientsService); + + signatoryName = ''; + cifNumber = ''; + panNumber = ''; + entityName = ''; + dinNumber = ''; + + verifying = false; + verified = false; + failed = false; + errorOccurred = false; + errorMessage = ''; + failReason = ''; + + directorName = ''; + dinStatus = ''; + dinDesignation = ''; + verifiedDate = new Date().toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); + + /** Companies list from DIN verification response */ + companies: any[] = []; + /** Whether entity name matched a company */ + companyMatch = false; + /** The matched company name */ + matchedCompany = ''; + + progress = 0; + currentStep = 0; + progressSteps = [ + { label: 'Connecting to MCA portal...', icon: 'globe' as any }, + { label: 'Looking up DIN records...', icon: 'search' as any }, + { label: 'Matching entity with companies...', icon: 'building' as any }, + { label: 'Saving verification...', icon: 'database' as any } + ]; + + private intervalId: any; + private apiResponse: any = null; + private apiError: any = null; + private apiDone = false; + + ngOnInit() { + this.signatoryName = this.data?.signatoryName || ''; + this.cifNumber = this.data?.cifNumber || ''; + this.panNumber = this.data?.panNumber || '-'; + this.entityName = this.data?.entityName || ''; + } + + onVerify() { + if (!this.dinNumber || this.dinNumber.length < 8) return; + + this.verifying = true; + this.verified = false; + this.failed = false; + this.errorOccurred = false; + this.apiDone = false; + this.apiResponse = null; + this.apiError = null; + this.progress = 0; + this.currentStep = 0; + this.companies = []; + this.companyMatch = false; + this.matchedCompany = ''; + + this.dialogRef.disableClose = true; + + // Fire API call — pass clientId, kycId, dinNumber and entityName + this.clientsService.verifyDIN(this.data.clientId, this.data.kycId, this.dinNumber, this.entityName).subscribe({ + next: (res: any) => { + this.apiResponse = res; + this.apiDone = true; + this.tryShowResult(); + }, + error: (err: any) => { + this.apiError = err; + this.apiDone = true; + this.tryShowResult(); + } + }); + + // Animate progress + this.intervalId = setInterval(() => { + this.progress += 2; + this.currentStep = Math.min(Math.floor(this.progress / 25), this.progressSteps.length - 1); + if (this.progress >= 100) { + clearInterval(this.intervalId); + this.intervalId = null; + this.tryShowResult(); + } + }, 60); + } + + private tryShowResult() { + if (!this.apiDone || this.progress < 100) return; + + this.verifying = false; + this.dialogRef.disableClose = false; + + if (this.apiError) { + this.errorOccurred = true; + this.errorMessage = + this.apiError?.error?.defaultUserMessage || + this.apiError?.error?.message || + this.apiError?.message || + 'Unable to verify DIN. Please try again.'; + return; + } + + const res = this.apiResponse; + const changes = res?.changes || res; + + if (changes?.dinVerified || res?.dinVerified) { + this.verified = true; + this.directorName = changes?.directorName || res?.directorName || ''; + this.dinStatus = changes?.dinStatus || res?.dinStatus || 'Active'; + this.dinDesignation = changes?.dinDesignation || res?.dinDesignation || ''; + this.verifiedDate = new Date().toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); + + // Process company list from response + const rawCompanies = changes?.companies || res?.companies || []; + this.companies = rawCompanies.map((c: any) => ({ + ...c, + companyName: c.companyName || c.name || '', + cin: c.cin || c.cinNumber || '', + designation: c.designation || '', + appointmentDate: c.appointmentDate || c.dateOfAppointment || '', + _matched: false + })); + + // Match entity name against company list + this.matchCompanyName(); + } else { + this.failed = true; + this.failReason = + changes?.dinFailReason || + res?.failReason || + res?.defaultUserMessage || + 'DIN could not be verified against MCA records.'; + } + } + + /** Match the entity name against the list of companies from the DIN response */ + private matchCompanyName() { + if (!this.entityName || this.companies.length === 0) return; + + const normalize = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, ''); + const entityNorm = normalize(this.entityName); + + for (const company of this.companies) { + const compNorm = normalize(company.companyName); + // Exact match or substring containment + if (compNorm === entityNorm || compNorm.includes(entityNorm) || entityNorm.includes(compNorm)) { + company._matched = true; + this.companyMatch = true; + this.matchedCompany = company.companyName; + break; + } + } + } + + onSkip() { + this.dialogRef.close({ skipped: true }); + } + + onRetry() { + this.verified = false; + this.failed = false; + this.errorOccurred = false; + this.progress = 0; + this.currentStep = 0; + this.companies = []; + this.companyMatch = false; + this.matchedCompany = ''; + } + + onClose(verified: boolean) { + this.dialogRef.close({ + verified, + dinNumber: this.dinNumber, + directorName: this.directorName, + dinStatus: this.dinStatus, + dinDesignation: this.dinDesignation, + dinVerified: verified, + companyMatch: this.companyMatch, + matchedCompany: this.matchedCompany, + companies: this.companies, + response: this.apiResponse + }); + } + + ngOnDestroy() { + if (this.intervalId) { + clearInterval(this.intervalId); + } + } +} diff --git a/src/app/clients/client-stepper/client-general-step/sanctions-verify-dialog.component.ts b/src/app/clients/client-stepper/client-general-step/sanctions-verify-dialog.component.ts index 10fb27d39..43100b447 100644 --- a/src/app/clients/client-stepper/client-general-step/sanctions-verify-dialog.component.ts +++ b/src/app/clients/client-stepper/client-general-step/sanctions-verify-dialog.component.ts @@ -1,3 +1,11 @@ +/** + * 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, OnDestroy, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatDialogRef, MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog'; @@ -21,7 +29,10 @@ import { ClientsService } from 'app/clients/clients.service';
@for (step of progressSteps; track step.label; let i = $index) {
- + {{ step.label }}
} @@ -42,8 +53,8 @@ import { ClientsService } from 'app/clients/clients.service';

No Sanctions Match Found

{{ clientName }} has been screened against the - UN Security Council Consolidated List and - FIU-IND records. No confirmed match was found. + UN Security Council Consolidated List and FIU-IND records. No confirmed + match was found.

@@ -550,7 +561,12 @@ export class SanctionsVerifyDialogComponent implements OnInit, OnDestroy { } private runProgress() { - const stepDurations = [1200, 1500, 1800, 1000]; // ms per step + const stepDurations = [ + 1200, + 1500, + 1800, + 1000 + ]; // ms per step const totalDuration = stepDurations.reduce((a, b) => a + b, 0); let elapsed = 0; const interval = 50; @@ -602,7 +618,10 @@ export class SanctionsVerifyDialogComponent implements OnInit, OnDestroy { if (this.apiError) { this.errorOccurred = true; - this.errorMessage = this.apiError?.error?.message || this.apiError?.message || 'Unable to connect to sanctions screening service.'; + this.errorMessage = + this.apiError?.error?.message || + this.apiError?.message || + 'Unable to connect to sanctions screening service.'; } else { this.screeningLogId = this.apiResult?.screeningLogId || null; if (this.apiResult?.matchFound) { diff --git a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.html b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.html index 09e6f73cf..879172463 100644 --- a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.html +++ b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.html @@ -20,15 +20,18 @@ - - 1 Permanent Address - + 1 Permanent Address
Address Line 1 home - +
@@ -45,9 +48,15 @@ PIN Code pin_drop - + (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" + /> @if (permanentForm.get('pinCode')?.hasError('pattern') && permanentForm.get('pinCode')?.touched) { Enter a valid 6-digit PIN code } @@ -126,9 +135,7 @@ - - 2 Current / Residential - + 2 Current / Residential
@@ -140,7 +147,12 @@
Address Line 1 - +
@@ -155,9 +167,15 @@
PIN Code - + (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" + /> @if (currentForm.get('pinCode')?.hasError('pattern') && currentForm.get('pinCode')?.touched) { Enter a valid 6-digit PIN code } @@ -230,9 +248,7 @@ - - 3 Office / Work - + 3 Office / Work
@@ -268,9 +284,14 @@ PIN Code pin_drop - + (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" + /> @if (officeForm.get('pinCode')?.hasError('pattern') && officeForm.get('pinCode')?.touched) { Enter a valid 6-digit PIN code } @@ -348,13 +369,13 @@ - - 4 Correspondence - + 4 Correspondence
Copy address from: - +
@@ -363,7 +384,12 @@ Address Line 1 home - +
@@ -380,9 +406,15 @@ PIN Code pin_drop - + (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" + /> @if (correspondenceForm.get('pinCode')?.hasError('pattern') && correspondenceForm.get('pinCode')?.touched) { Enter a valid 6-digit PIN code } diff --git a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.scss b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.scss index 2b0ca9c9a..b4638207c 100644 --- a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.scss +++ b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.scss @@ -1,3 +1,11 @@ +/** + * 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/. + */ + /** * Copyright since 2025 Mifos Initiative */ @@ -38,7 +46,7 @@ $muted: #64748b; &__sub { font-size: 11px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); margin: 2px 0 0; } } @@ -76,9 +84,17 @@ $muted: #64748b; gap: 4px 16px; margin-bottom: 4px; - &--1 { grid-template-columns: 1fr; } - &--2 { grid-template-columns: 1fr 1fr; } - &--3 { grid-template-columns: 1fr 1fr 1fr; } + &--1 { + grid-template-columns: 1fr; + } + + &--2 { + grid-template-columns: 1fr 1fr; + } + + &--3 { + grid-template-columns: 1fr 1fr 1fr; + } } .addr-ff { @@ -101,7 +117,8 @@ $muted: #64748b; white-space: nowrap; } - &::before, &::after { + &::before, + &::after { content: ''; flex: 1; height: 1px; @@ -173,8 +190,9 @@ $muted: #64748b; } // Responsive -@media (max-width: 768px) { - .addr-grid--2, .addr-grid--3 { +@media (width <= 768px) { + .addr-grid--2, + .addr-grid--3 { grid-template-columns: 1fr; } diff --git a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts index 6bbe82071..935ac2b63 100644 --- a/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts +++ b/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.ts @@ -42,27 +42,60 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { sameAsPermanent = false; permanentForm: UntypedFormGroup = this.fb.group({ - addressLine1: ['', Validators.required], + addressLine1: [ + '', + Validators.required + ], addressLine2: [''], - pinCode: ['', [Validators.required, Validators.pattern(/^\d{6}$/)]], - locality: ['', Validators.required], + pinCode: [ + '', + [ + Validators.required, + Validators.pattern(/^\d{6}$/) + ] + ], + locality: [ + '', + Validators.required + ], city: [''], district: [''], - state: ['', Validators.required], + state: [ + '', + Validators.required + ], country: [{ value: 'India', disabled: true }], landmark: [''], - proofType: ['', Validators.required], + proofType: [ + '', + Validators.required + ], proofDocNumber: [''] }); currentForm: UntypedFormGroup = this.fb.group({ - addressLine1: ['', Validators.required], + addressLine1: [ + '', + Validators.required + ], addressLine2: [''], - pinCode: ['', [Validators.required, Validators.pattern(/^\d{6}$/)]], - locality: ['', Validators.required], + pinCode: [ + '', + [ + Validators.required, + Validators.pattern(/^\d{6}$/) + ] + ], + locality: [ + '', + Validators.required + ], city: [''], district: [''], - state: ['', Validators.required], + state: [ + '', + Validators.required + ], country: [{ value: 'India', disabled: true }], landmark: [''], residenceType: [''], @@ -74,7 +107,10 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { department: [''], addressLine1: [''], addressLine2: [''], - pinCode: ['', Validators.pattern(/^\d{6}$/)], + pinCode: [ + '', + Validators.pattern(/^\d{6}$/) + ], locality: [''], city: [''], district: [''], @@ -87,13 +123,28 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { }); correspondenceForm: UntypedFormGroup = this.fb.group({ - addressLine1: ['', Validators.required], + addressLine1: [ + '', + Validators.required + ], addressLine2: [''], - pinCode: ['', [Validators.required, Validators.pattern(/^\d{6}$/)]], - locality: ['', Validators.required], + pinCode: [ + '', + [ + Validators.required, + Validators.pattern(/^\d{6}$/) + ] + ], + locality: [ + '', + Validators.required + ], city: [''], district: [''], - state: ['', Validators.required], + state: [ + '', + Validators.required + ], country: [{ value: 'India', disabled: true }], landmark: [''], commMode: [''], @@ -116,18 +167,36 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { }; proofTypes = [ - 'Aadhaar Card', 'Passport', 'Voter ID', 'Driving Licence', - 'Utility Bill (not older than 3 months)', 'Bank Statement' + 'Aadhaar Card', + 'Passport', + 'Voter ID', + 'Driving Licence', + 'Utility Bill (not older than 3 months)', + 'Bank Statement' ]; - residenceTypes = ['Owned', 'Rented', 'Leased', 'Company Provided', 'Parental / Family Owned']; + residenceTypes = [ + 'Owned', + 'Rented', + 'Leased', + 'Company Provided', + 'Parental / Family Owned' + ]; employmentTypes = [ - 'Salaried (Private)', 'Salaried (Government / PSU)', - 'Self-Employed Professional', 'Business Owner', 'Retired' + 'Salaried (Private)', + 'Salaried (Government / PSU)', + 'Self-Employed Professional', + 'Business Owner', + 'Retired' ]; - commModes = ['Post / Courier', 'Email', 'SMS', 'WhatsApp']; + commModes = [ + 'Post / Courier', + 'Email', + 'SMS', + 'WhatsApp' + ]; private pinTimers: Record = {}; private pinCache: Record = {}; @@ -144,7 +213,12 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if (changes['readOnly']) { - const forms = [this.permanentForm, this.currentForm, this.officeForm, this.correspondenceForm]; + const forms = [ + this.permanentForm, + this.currentForm, + this.officeForm, + this.correspondenceForm + ]; for (const form of forms) { if (this.readOnly) { form.disable({ emitEvent: false }); @@ -168,10 +242,19 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { // Build type-to-tab mapping from address type names const tabMap: Record = { - permanent: 'permanent', 'permanent address': 'permanent', - residential: 'current', current: 'current', 'current / residential': 'current', communication: 'current', - office: 'office', 'office / work': 'office', business: 'office', work: 'office', - correspondence: 'correspondence', mailing: 'correspondence', postal: 'correspondence' + permanent: 'permanent', + 'permanent address': 'permanent', + residential: 'current', + current: 'current', + 'current / residential': 'current', + communication: 'current', + office: 'office', + 'office / work': 'office', + business: 'office', + work: 'office', + correspondence: 'correspondence', + mailing: 'correspondence', + postal: 'correspondence' }; for (const addr of addresses) { @@ -192,7 +275,9 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { }); } }, - error: () => { this.addressLoaded = true; } + error: () => { + this.addressLoaded = true; + } }); } @@ -235,11 +320,13 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { private applyPinData(tab: string, offices: any[]) { const seen = new Set(); - this.localityOptions[tab] = offices.filter(o => { - if (seen.has(o.Name)) return false; - seen.add(o.Name); - return true; - }).map(o => o.Name); + this.localityOptions[tab] = offices + .filter((o) => { + if (seen.has(o.Name)) return false; + seen.add(o.Name); + return true; + }) + .map((o) => o.Name); const form = this.getForm(tab); const f = offices[0]; @@ -259,11 +346,16 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { getForm(tab: string): UntypedFormGroup { switch (tab) { - case 'permanent': return this.permanentForm; - case 'current': return this.currentForm; - case 'office': return this.officeForm; - case 'correspondence': return this.correspondenceForm; - default: return this.permanentForm; + case 'permanent': + return this.permanentForm; + case 'current': + return this.currentForm; + case 'office': + return this.officeForm; + case 'correspondence': + return this.correspondenceForm; + default: + return this.permanentForm; } } @@ -307,7 +399,9 @@ export class ClientIndiaAddressStepComponent implements OnInit, OnChanges { get addressData(): any { return { permanent: this.permanentForm.getRawValue(), - current: this.sameAsPermanent ? { ...this.permanentForm.getRawValue(), sameAsPermanent: true } : this.currentForm.getRawValue(), + current: this.sameAsPermanent + ? { ...this.permanentForm.getRawValue(), sameAsPermanent: true } + : this.currentForm.getRawValue(), office: this.officeForm.getRawValue(), correspondence: this.correspondenceForm.getRawValue() }; 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 daee1a515..429978f87 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 @@ -305,23 +305,85 @@ - - - GSTIN Number - - @if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) { - Invalid GSTIN format (e.g. 22AAAAA0000A1Z5) - } - + +
+ + GSTIN Number + receipt_long + + @if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) { + Invalid GSTIN format (e.g. 22AAAAA0000A1Z5) + } + @if (gstinVerifyStatus === 'VERIFIED') { + check_circle + } + @if (gstinVerifyStatus === 'FAILED') { + cancel + } + +
+ @if (gstinVerifyStatus === 'NONE' || gstinVerifyStatus === 'FAILED') { + + } + @if (gstinVerifyStatus === 'VERIFIED') { + Verified + } +
+
+ @if (gstinVerifyStatus === 'VERIFIED') { +
+
+ + GSTIN Verified Successfully +
+
+ @if (gstinVerifyName) { +
+ Trade Name / Legal Name + {{ gstinVerifyName }} +
+ } + @if (gstinVerifyState) { +
+ State + {{ gstinVerifyState }} +
+ } +
+ Verified On + {{ gstinVerifiedOn }} +
+
+
+ } @if (constitutionMeta?.value === 'PROPRIETORSHIP') { 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 857797484..288384984 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 @@ -263,6 +263,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { entPanVerifyName = ''; entPanVerifiedOn = ''; + /** GSTIN verification */ + gstinVerifyStatus: 'NONE' | 'VERIFIED' | 'FAILED' = 'NONE'; + gstinVerifying = false; + gstinVerifyName = ''; + gstinVerifiedOn = ''; + gstinVerifyState = ''; + /** CIN verification (dummy) */ cinVerifyStatus: 'NONE' | 'VERIFIED' | 'FAILED' = 'NONE'; cinVerifying = false; @@ -480,6 +487,17 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { if (data.panAadhaarSeedingStatus) { this.panAadhaarSeedingStatus = data.panAadhaarSeedingStatus; } + if (data.gstinVerified) { + this.gstinVerifyStatus = 'VERIFIED'; + this.gstinVerifyName = data.gstinTradeName || data.gstinLegalName || ''; + this.gstinVerifyState = data.gstinState || ''; + const rawGstinDate = data.gstinVerifiedOn; + if (Array.isArray(rawGstinDate) && rawGstinDate.length >= 3) { + this.gstinVerifiedOn = `${rawGstinDate[2]?.toString().padStart(2, '0')}/${rawGstinDate[1]?.toString().padStart(2, '0')}/${rawGstinDate[0]}`; + } else if (rawGstinDate) { + this.gstinVerifiedOn = rawGstinDate; + } + } } // Load existing photo & signature this.loadExistingPhoto(); @@ -772,6 +790,71 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy { ); } + /** Verify GSTIN via API */ + verifyGstin() { + if (!this.clientId) return; + const gstin = this.kycForm.value.gstinNumber; + if (!gstin || this.kycForm.get('gstinNumber')?.invalid) return; + + this.gstinVerifying = true; + const dateFormat = this.settingsService.dateFormat; + + const payload: any = { + locale: 'en', + kycType: 1, + riskCategory: this.kycForm.value.riskCategory || 1, + gstinNumber: gstin, + dateFormat, + panNumber: this.kycForm.value.enterprisePan || this.kycForm.value.panNumber || null, + aadhaarNumberMasked: 'XXXXXXXX0000' + }; + + this.saveKycAndDo( + payload, + () => { + this.clientsService.verifyGSTIN(this.clientId, this.kycId).subscribe({ + next: (res: any) => { + this.gstinVerifying = false; + const changes = res?.changes || res; + if (changes?.gstinVerified === true) { + this.gstinVerifyStatus = 'VERIFIED'; + this.gstinVerifyName = changes?.gstinTradeName || changes?.gstinLegalName || ''; + this.gstinVerifyState = changes?.gstinState || ''; + const rawDate = changes?.gstinVerifiedOn; + if (Array.isArray(rawDate)) { + this.gstinVerifiedOn = `${rawDate[2]?.toString().padStart(2, '0')}/${rawDate[1]?.toString().padStart(2, '0')}/${rawDate[0]}`; + } else { + const now = new Date(); + this.gstinVerifiedOn = + rawDate || + `${now.getDate().toString().padStart(2, '0')}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getFullYear()}`; + } + } else { + this.gstinVerifyStatus = 'FAILED'; + this.showPanErrorAlert( + changes?.gstinVerificationMessage || + 'GSTIN verification failed. Please check the GSTIN number and try again.' + ); + } + }, + error: (err: any) => { + this.gstinVerifying = false; + this.gstinVerifyStatus = 'FAILED'; + this.showPanErrorAlert( + err?.error?.errors?.[0]?.defaultUserMessage || + err?.error?.defaultUserMessage || + 'GSTIN verification failed.' + ); + } + }); + }, + (msg) => { + this.gstinVerifying = false; + this.showPanErrorAlert(msg); + } + ); + } + /** Verify CIN (dummy — no real API) */ verifyCin() { const cin = this.kycForm.value.cinNumber; diff --git a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.html b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.html index 80d2260ba..c3d248f03 100644 --- a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.html +++ b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.html @@ -1,3 +1,11 @@ + + @@ -20,11 +28,13 @@
- + (mouseleave)="onMouseUp()" + >
Drag to move. Drag corner to resize.
diff --git a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.scss b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.scss index b856b0a44..a2fb27009 100644 --- a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.scss +++ b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.scss @@ -1,3 +1,11 @@ +/** + * 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/. + */ + /** * Copyright since 2025 Mifos Initiative */ @@ -158,7 +166,7 @@ $navy: #0f172a; } } -@media (max-width: 600px) { +@media (width <= 600px) { .crop-workspace { flex-direction: column; } diff --git a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.ts b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.ts index 4192ddbdf..75140c573 100644 --- a/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.ts +++ b/src/app/clients/client-stepper/client-kyc-step/image-crop-dialog/image-crop-dialog.component.ts @@ -1,8 +1,12 @@ /** * 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, Inject, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { Component, inject, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef, MatDialogContent, MatDialogActions } from '@angular/material/dialog'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @@ -59,10 +63,8 @@ export class ImageCropDialogComponent implements AfterViewInit { zoom = 1; - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: ImageCropData - ) {} + dialogRef = inject(MatDialogRef); + data = inject(MAT_DIALOG_DATA); ngAfterViewInit() { const reader = new FileReader(); @@ -127,9 +129,14 @@ export class ImageCropDialogComponent implements AfterViewInit { ctx.clearRect(this.cropX, this.cropY, this.cropW, this.cropH); ctx.drawImage( this.img, - this.cropX * this.scaleX, this.cropY * this.scaleY, - this.cropW * this.scaleX, this.cropH * this.scaleY, - this.cropX, this.cropY, this.cropW, this.cropH + this.cropX * this.scaleX, + this.cropY * this.scaleY, + this.cropW * this.scaleX, + this.cropH * this.scaleY, + this.cropX, + this.cropY, + this.cropW, + this.cropH ); // Crop border @@ -141,14 +148,31 @@ export class ImageCropDialogComponent implements AfterViewInit { const hs = 8; ctx.fillStyle = '#1353a0'; const corners = [ - [this.cropX, this.cropY], - [this.cropX + this.cropW, this.cropY], - [this.cropX, this.cropY + this.cropH], - [this.cropX + this.cropW, this.cropY + this.cropH] + [ + this.cropX, + this.cropY + ], + [ + this.cropX + this.cropW, + this.cropY + ], + [ + this.cropX, + this.cropY + this.cropH + ], + [ + this.cropX + this.cropW, + this.cropY + this.cropH + ] ]; - corners.forEach(([x, y]) => { - ctx.fillRect(x - hs / 2, y - hs / 2, hs, hs); - }); + corners.forEach( + ([ + x, + y + ]) => { + ctx.fillRect(x - hs / 2, y - hs / 2, hs, hs); + } + ); } private updatePreview() { @@ -163,9 +187,14 @@ export class ImageCropDialogComponent implements AfterViewInit { pCtx.clearRect(0, 0, pw, ph); pCtx.drawImage( this.img, - this.cropX * this.scaleX, this.cropY * this.scaleY, - this.cropW * this.scaleX, this.cropH * this.scaleY, - 0, 0, pw, ph + this.cropX * this.scaleX, + this.cropY * this.scaleY, + this.cropW * this.scaleX, + this.cropH * this.scaleY, + 0, + 0, + pw, + ph ); } @@ -229,9 +258,14 @@ export class ImageCropDialogComponent implements AfterViewInit { const ctx = offscreen.getContext('2d')!; ctx.drawImage( this.img, - this.cropX * this.scaleX, this.cropY * this.scaleY, - this.cropW * this.scaleX, this.cropH * this.scaleY, - 0, 0, offscreen.width, offscreen.height + this.cropX * this.scaleX, + this.cropY * this.scaleY, + this.cropW * this.scaleX, + this.cropH * this.scaleY, + 0, + 0, + offscreen.width, + offscreen.height ); offscreen.toBlob((blob) => { diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html index 5299d941b..f262f460d 100644 --- a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html @@ -88,14 +88,18 @@ } @if (client?.dateOfBirth) {
- {{ client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date' }} + {{ + client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date' + }} {{ client.dateOfBirth | dateFormat }}
} @if (client?.legalFormId === LegalFormId.PERSON && client?.genderId) {
Gender - {{ client.genderId | find: clientTemplate.genderOptions : 'id' : 'name' }} + {{ + client.genderId | find: clientTemplate.genderOptions : 'id' : 'name' + }}
} @if (client?.legalFormId) { @@ -107,13 +111,17 @@ @if (client?.officeId) {
Office - {{ client.officeId | find: clientTemplate.officeOptions : 'id' : 'name' }} + {{ + client.officeId | find: clientTemplate.officeOptions : 'id' : 'name' + }}
} @if (client?.staffId) {
Staff - {{ client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName' }} + {{ + client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName' + }}
} @if (client?.legalFormId === LegalFormId.PERSON) { @@ -137,7 +145,9 @@ @if (client?.salutationId) {
Salutation - {{ client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name' }} + {{ + client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name' + }}
} @if (client?.ageYears != null) { @@ -150,7 +160,11 @@
Minor - + {{ client.isMinor ? 'Yes (Age < 10)' : 'No' }} @@ -172,13 +186,19 @@ @if (client.clientNonPersonDetails.constitutionId) {
Constitution - {{ client.clientNonPersonDetails.constitutionId | find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name' }} + {{ + client.clientNonPersonDetails.constitutionId + | find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name' + }}
} @if (client.clientNonPersonDetails.mainBusinessLineId) {
Main Business Line - {{ client.clientNonPersonDetails.mainBusinessLineId | find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name' }} + {{ + client.clientNonPersonDetails.mainBusinessLineId + | find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name' + }}
} @if (client.clientNonPersonDetails.incorpNumber) { @@ -215,7 +235,11 @@
DIP Verification - + {{ client?.dipVerified ? 'Verified' : 'Pending' }} @@ -236,7 +260,13 @@
Alternate DIP Verification - + {{ client?.alternateDipVerified ? 'Verified' : 'Pending' }} @@ -257,8 +287,18 @@
Name Verification (Sanctions) - - {{ client?.nameVerified ? 'Verified — Clear' : (client?.nameVerified === false ? 'Sanctions Match' : 'Pending') }} + + {{ + client?.nameVerified + ? 'Verified — Clear' + : client?.nameVerified === false + ? 'Sanctions Match' + : 'Pending' + }}
@@ -274,61 +314,82 @@ @if (client?.legalFormId !== 2) { -
-
- - Financial & Classification -
-
-
- @if (client?.occupationCategoryId) { -
- Occupation Category - {{ client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name' }} -
- } - @if (client?.occupationId) { -
- Occupation - {{ client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name' }} -
- } - @if (client?.clientTypeId) { -
- Client Type - {{ client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name' }} -
- } - @if (client?.clientClassificationId) { -
- Client Classification - {{ client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name' }} -
- } - @if (client?.monthlyIncome) { -
- Monthly Income - ₹ {{ client.monthlyIncome | number }} -
- } - @if (client?.annualTurnover) { -
- Annual Turnover - ₹ {{ client.annualTurnover | number }} -
- } - @if (!client?.occupationCategoryId && !client?.occupationId && !client?.clientTypeId && !client?.clientClassificationId && !client?.monthlyIncome && !client?.annualTurnover) { -
- No classification data provided -
- } +
+
+ + Financial & Classification +
+
+
+ @if (client?.occupationCategoryId) { +
+ Occupation Category + {{ + client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name' + }} +
+ } + @if (client?.occupationId) { +
+ Occupation + {{ + client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name' + }} +
+ } + @if (client?.clientTypeId) { +
+ Client Type + {{ + client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name' + }} +
+ } + @if (client?.clientClassificationId) { +
+ Client Classification + {{ + client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name' + }} +
+ } + @if (client?.monthlyIncome) { +
+ Monthly Income + ₹ {{ client.monthlyIncome | number }} +
+ } + @if (client?.annualTurnover) { +
+ Annual Turnover + ₹ {{ client.annualTurnover | number }} +
+ } + @if ( + !client?.occupationCategoryId && + !client?.occupationId && + !client?.clientTypeId && + !client?.clientClassificationId && + !client?.monthlyIncome && + !client?.annualTurnover + ) { +
+ No classification data provided +
+ } +
-
} - @if (kycData?.panNumber || kycData?.aadhaarNumberMasked || kycData?.gstinNumber || kycData?.enterprisePan || kycData?.cinNumber) { + @if ( + kycData?.panNumber || + kycData?.aadhaarNumberMasked || + kycData?.gstinNumber || + kycData?.enterprisePan || + kycData?.cinNumber + ) {
@@ -391,9 +452,13 @@
Risk Category - @if (kycData.riskCategory === 1) { Low Risk } - @else if (kycData.riskCategory === 2) { Medium Risk } - @else if (kycData.riskCategory === 3) { High Risk } + @if (kycData.riskCategory === 1) { + Low Risk + } @else if (kycData.riskCategory === 2) { + Medium Risk + } @else if (kycData.riskCategory === 3) { + High Risk + }
} @@ -414,7 +479,7 @@
Guardian Details - Minor + Minor
@@ -450,7 +515,13 @@
Status - + {{ guardian.status.value }} @@ -524,7 +595,12 @@
} } - @if (!addressData.permanent?.addressLine1 && !addressData.current?.addressLine1 && !addressData.office?.addressLine1 && !addressData.correspondence?.addressLine1) { + @if ( + !addressData.permanent?.addressLine1 && + !addressData.current?.addressLine1 && + !addressData.office?.addressLine1 && + !addressData.correspondence?.addressLine1 + ) {
No address data provided
@@ -563,40 +639,76 @@
@if (entityClassificationData.msme.udyamRegistrationNumber) { -
Udyam Registration No.{{ entityClassificationData.msme.udyamRegistrationNumber }}
+
+ Udyam Registration No.{{ entityClassificationData.msme.udyamRegistrationNumber }} +
} @if (entityClassificationData.msme.msmeClassification) { -
MSME Classification{{ entityClassificationData.msme.msmeClassification }}
+
+ MSME Classification{{ entityClassificationData.msme.msmeClassification }} +
} @if (entityClassificationData.msme.enterpriseType) { -
Enterprise Type{{ entityClassificationData.msme.enterpriseType }}
+
+ Enterprise Type{{ entityClassificationData.msme.enterpriseType }} +
} @if (entityClassificationData.msme.nicActivityCode) { -
NIC Activity Code{{ entityClassificationData.msme.nicActivityCode }}
+
+ NIC Activity Code{{ entityClassificationData.msme.nicActivityCode }} +
} @if (entityClassificationData.msme.annualTurnover) { -
Annual Turnover₹ {{ entityClassificationData.msme.annualTurnover }} Cr
+
+ Annual Turnover₹ {{ entityClassificationData.msme.annualTurnover }} Cr +
} @if (entityClassificationData.msme.investmentPlantMachinery) { -
Investment (Plant & Machinery)₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr
+
+ Investment (Plant & Machinery)₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr +
} @if (entityClassificationData.msme.financialYearAssessment) { -
FY of Assessment{{ entityClassificationData.msme.financialYearAssessment }}
+
+ FY of Assessment{{ entityClassificationData.msme.financialYearAssessment }} +
} @if (entityClassificationData.msme.socialCategoryOwner) { -
Social Category{{ entityClassificationData.msme.socialCategoryOwner }}
+
+ Social Category{{ entityClassificationData.msme.socialCategoryOwner }} +
} @if (entityClassificationData.msme.genderPrimaryOwner) { -
Gender of Owner{{ entityClassificationData.msme.genderPrimaryOwner }}
+
+ Gender of Owner{{ entityClassificationData.msme.genderPrimaryOwner }} +
} @if (entityClassificationData.msme.noOfEmployees) { -
No. of Employees{{ entityClassificationData.msme.noOfEmployees }}
+
+ No. of Employees{{ entityClassificationData.msme.noOfEmployees }} +
} @if (entityClassificationData.msme.womenLedEnterprise) { -
Women-led EnterpriseYes
+
+ Women-led EnterpriseYes +
} @if (entityClassificationData.msme.divyangPwdOwned) { -
Divyang (PWD) OwnedYes
+
+ Divyang (PWD) OwnedYes +
}
@@ -613,19 +725,34 @@
@if (entityClassificationData.startup.dpiitRecognitionNumber) { -
DPIIT Recognition No.{{ entityClassificationData.startup.dpiitRecognitionNumber }}
+
+ DPIIT Recognition No.{{ entityClassificationData.startup.dpiitRecognitionNumber }} +
} @if (entityClassificationData.startup.entityStage) { -
Entity Stage{{ entityClassificationData.startup.entityStage }}
+
+ Entity Stage{{ entityClassificationData.startup.entityStage }} +
} @if (entityClassificationData.startup.sectorAsDpiit) { -
Sector (DPIIT){{ entityClassificationData.startup.sectorAsDpiit }}
+
+ Sector (DPIIT){{ entityClassificationData.startup.sectorAsDpiit }} +
} @if (entityClassificationData.startup.taxExemption80iac) { -
80IAC Tax ExemptionYes
+
+ 80IAC Tax ExemptionYes +
} @if (entityClassificationData.startup.pslLoanLimit) { -
PSL Loan Limit₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr
+
+ PSL Loan Limit₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr +
}
@@ -642,22 +769,40 @@
@if (entityClassificationData.mudra.mudraCategory) { -
Mudra Category{{ entityClassificationData.mudra.mudraCategory }}
+
+ Mudra Category{{ entityClassificationData.mudra.mudraCategory }} +
} @if (entityClassificationData.mudra.mudraLoanAccountNo) { -
Loan Account No.{{ entityClassificationData.mudra.mudraLoanAccountNo }}
+
+ Loan Account No.{{ entityClassificationData.mudra.mudraLoanAccountNo }} +
} @if (entityClassificationData.mudra.sanctionedAmount) { -
Sanctioned Amount₹ {{ entityClassificationData.mudra.sanctionedAmount | number }}
+
+ Sanctioned Amount₹ {{ entityClassificationData.mudra.sanctionedAmount | number }} +
} @if (entityClassificationData.mudra.lendingInstitutionName) { -
Lending Institution{{ entityClassificationData.mudra.lendingInstitutionName }}
+
+ Lending Institution{{ entityClassificationData.mudra.lendingInstitutionName }} +
} @if (entityClassificationData.mudra.repaymentStatus) { -
Repayment Status{{ entityClassificationData.mudra.repaymentStatus }}
+
+ Repayment Status{{ entityClassificationData.mudra.repaymentStatus }} +
} @if (entityClassificationData.mudra.npaFlag) { -
NPA FlagNPA
+
+ NPA FlagNPA +
}
@@ -674,19 +819,36 @@
@if (entityClassificationData.eou.eouRegistrationNumber) { -
EOU Registration No.{{ entityClassificationData.eou.eouRegistrationNumber }}
+
+ EOU Registration No.{{ entityClassificationData.eou.eouRegistrationNumber }} +
} @if (entityClassificationData.eou.registeredDcZone) { -
Registered DC Zone{{ entityClassificationData.eou.registeredDcZone }}
+
+ Registered DC Zone{{ entityClassificationData.eou.registeredDcZone }} +
} @if (entityClassificationData.eou.gstinEou) { -
GSTIN (EOU){{ entityClassificationData.eou.gstinEou }}
+
+ GSTIN (EOU){{ entityClassificationData.eou.gstinEou }} +
} @if (entityClassificationData.eou.annualExportObligation) { -
Export Obligation₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr
+
+ Export Obligation₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr +
} @if (entityClassificationData.eou.adCategoryBankCode) { -
AD Bank Code{{ entityClassificationData.eou.adCategoryBankCode }}
+
+ AD Bank Code{{ entityClassificationData.eou.adCategoryBankCode }} +
}
@@ -703,22 +865,40 @@
@if (entityClassificationData.importexport.iecCode) { -
IEC Code{{ entityClassificationData.importexport.iecCode }}
+
+ IEC Code{{ entityClassificationData.importexport.iecCode }} +
} @if (entityClassificationData.importexport.iecStatus) { -
IEC Status{{ entityClassificationData.importexport.iecStatus }}
+
+ IEC Status{{ entityClassificationData.importexport.iecStatus }} +
} @if (entityClassificationData.importexport.tradeType) { -
Trade Type{{ entityClassificationData.importexport.tradeType }}
+
+ Trade Type{{ entityClassificationData.importexport.tradeType }} +
} @if (entityClassificationData.importexport.primaryHsCode) { -
Primary HS Code{{ entityClassificationData.importexport.primaryHsCode }}
+
+ Primary HS Code{{ entityClassificationData.importexport.primaryHsCode }} +
} @if (entityClassificationData.importexport.eefcAccountRequired) { -
EEFC AccountRequired
+
+ EEFC AccountRequired +
} @if (entityClassificationData.importexport.femaDeclarationObtained) { -
FEMA DeclarationObtained
+
+ FEMA DeclarationObtained +
}
@@ -735,22 +915,40 @@
@if (entityClassificationData.psl.pslCategory) { -
PSL Category{{ entityClassificationData.psl.pslCategory }}
+
+ PSL Category{{ entityClassificationData.psl.pslCategory }} +
} @if (entityClassificationData.psl.pslSubCategory) { -
PSL Sub-category{{ entityClassificationData.psl.pslSubCategory }}
+
+ PSL Sub-category{{ entityClassificationData.psl.pslSubCategory }} +
} @if (entityClassificationData.psl.pslSanctionedLimit) { -
Sanctioned Limit₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }}
+
+ Sanctioned Limit₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }} +
} @if (entityClassificationData.psl.fiddReportingCode) { -
FIDD Reporting Code{{ entityClassificationData.psl.fiddReportingCode }}
+
+ FIDD Reporting Code{{ entityClassificationData.psl.fiddReportingCode }} +
} @if (entityClassificationData.psl.weakerSectionFlag) { -
Weaker SectionYes
+
+ Weaker SectionYes +
} @if (entityClassificationData.psl.womenEntrepreneurFlag) { -
Women EntrepreneurYes
+
+ Women EntrepreneurYes +
}
@@ -767,25 +965,46 @@
@if (entityClassificationData.nps.industrySector) { -
Industry Sector{{ entityClassificationData.nps.industrySector }}
+
+ Industry Sector{{ entityClassificationData.nps.industrySector }} +
} @if (entityClassificationData.nps.nicCode) { -
NIC Code{{ entityClassificationData.nps.nicCode }}
+
+ NIC Code{{ entityClassificationData.nps.nicCode }} +
} @if (entityClassificationData.nps.annualRevenueBand) { -
Annual Revenue Band{{ entityClassificationData.nps.annualRevenueBand }}
+
+ Annual Revenue Band{{ entityClassificationData.nps.annualRevenueBand }} +
} @if (entityClassificationData.nps.riskCategory) { -
Risk Category{{ entityClassificationData.nps.riskCategory }}
+
+ Risk Category{{ entityClassificationData.nps.riskCategory }} +
} @if (entityClassificationData.nps.amlRiskClassification) { -
AML Risk{{ entityClassificationData.nps.amlRiskClassification }}
+
+ AML Risk{{ entityClassificationData.nps.amlRiskClassification }} +
} @if (entityClassificationData.nps.pepFlag) { -
PEP FlagPEP
+
+ PEP FlagPEP +
} @if (entityClassificationData.nps.amlWatchlistMatch) { -
AML WatchlistMatch Found
+
+ AML WatchlistMatch Found +
}
@@ -793,58 +1012,64 @@ } } - @if (client?.legalFormId !== 2) { -
-
- - Family Members - @if (familyMembers.length > 0) { - {{ familyMembers.length }} - } -
-
- @if (familyMembers.length === 0) { -
- warning - No family members added. At least one is required. -
- } @else { -
- @for (member of familyMembers; track member; let i = $index) { -
-
{{ i + 1 }}
-
- person -
-
- {{ member.firstName || member.displayName }} - - @if (member.relationship) { {{ member.relationship }} } - @if (member.dateOfBirth) { · DOB: {{ member.dateOfBirth }} } - -
- @if (member.aadhaarNumber || member.panNumber) { -
- @if (member.panNumber) { PAN: {{ member.panNumber }} } - @if (member.aadhaarNumber) { Aadhaar: {{ member.aadhaarNumber }} } +
+
+ + Family Members + @if (familyMembers.length > 0) { + {{ familyMembers.length }} + } +
+
+ @if (familyMembers.length === 0) { +
+ warning + No family members added. At least one is required. +
+ } @else { +
+ @for (member of familyMembers; track member; let i = $index) { +
+
{{ i + 1 }}
+
+ person
- } -
- @if (member.isDependent) { - Dependent - } - @if (member.linkedClientId) { - Linked +
+ {{ member.firstName || member.displayName }} + + @if (member.relationship) { + {{ member.relationship }} + } + @if (member.dateOfBirth) { + · DOB: {{ member.dateOfBirth }} + } + +
+ @if (member.aadhaarNumber || member.panNumber) { +
+ @if (member.panNumber) { + PAN: {{ member.panNumber }} + } + @if (member.aadhaarNumber) { + Aadhaar: {{ member.aadhaarNumber }} + } +
} +
+ @if (member.isDependent) { + Dependent + } + @if (member.linkedClientId) { + Linked + } +
-
- } -
- } + } +
+ } +
-
} -
diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss index 6fcd04dc7..a5e542600 100644 --- a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss @@ -32,7 +32,7 @@ &__sub { font-size: 11px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); margin: 2px 0 0; } } @@ -51,7 +51,7 @@ padding: 20px 24px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); border-radius: 10px; - border: 1px solid rgba(0, 0, 0, 0.06); + border: 1px solid rgb(0 0 0 / 6%); } .profile-avatar { @@ -96,9 +96,9 @@ font-weight: 500; padding: 3px 10px; border-radius: 20px; - background: rgba(255, 255, 255, 0.7); + background: rgb(255 255 255 / 70%); color: #555; - border: 1px solid rgba(0, 0, 0, 0.08); + border: 1px solid rgb(0 0 0 / 8%); .chip-icon { font-size: 10px; @@ -120,7 +120,7 @@ /* ── Info Cards ── */ .info-card { - border: 1px solid rgba(0, 0, 0, 0.08); + border: 1px solid rgb(0 0 0 / 8%); border-radius: 8px; overflow: hidden; background: #fff; @@ -132,7 +132,7 @@ gap: 8px; padding: 12px 20px; background: #fafafa; - border-bottom: 1px solid rgba(0, 0, 0, 0.06); + border-bottom: 1px solid rgb(0 0 0 / 6%); font-size: 14px; font-weight: 600; color: #333; @@ -177,8 +177,8 @@ flex-direction: column; gap: 4px; padding: 12px 16px; - border-bottom: 1px solid rgba(0, 0, 0, 0.04); - border-right: 1px solid rgba(0, 0, 0, 0.04); + border-bottom: 1px solid rgb(0 0 0 / 4%); + border-right: 1px solid rgb(0 0 0 / 4%); &:nth-child(3n) { border-right: none; @@ -195,7 +195,7 @@ font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; - color: rgba(0, 0, 0, 0.45); + color: rgb(0 0 0 / 45%); } .preview-value { @@ -204,7 +204,7 @@ color: #1a1a1a; &.no-data { - color: rgba(0, 0, 0, 0.3); + color: rgb(0 0 0 / 30%); font-style: italic; font-weight: 400; } @@ -239,7 +239,7 @@ border-collapse: collapse; tr { - border-bottom: 1px solid rgba(0, 0, 0, 0.04); + border-bottom: 1px solid rgb(0 0 0 / 4%); &:last-child { border-bottom: none; @@ -254,7 +254,7 @@ .info-label { width: 40%; font-size: 13px; - color: rgba(0, 0, 0, 0.5); + color: rgb(0 0 0 / 50%); font-weight: 500; padding-right: 16px; } @@ -278,17 +278,17 @@ padding: 10px 16px; font-size: 12px; font-weight: 600; - color: rgba(0, 0, 0, 0.55); + color: rgb(0 0 0 / 55%); text-align: left; text-transform: uppercase; letter-spacing: 0.3px; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); + border-bottom: 1px solid rgb(0 0 0 / 8%); } } tbody { tr { - border-bottom: 1px solid rgba(0, 0, 0, 0.04); + border-bottom: 1px solid rgb(0 0 0 / 4%); &:last-child { border-bottom: none; @@ -349,7 +349,7 @@ .address-block { margin-bottom: 16px; padding-bottom: 16px; - border-bottom: 1px dashed rgba(0, 0, 0, 0.08); + border-bottom: 1px dashed rgb(0 0 0 / 8%); &:last-child { margin-bottom: 0; @@ -381,7 +381,7 @@ justify-content: flex-end; gap: 12px; padding: 20px 0 4px; - border-top: 1px solid rgba(0, 0, 0, 0.08); + border-top: 1px solid rgb(0 0 0 / 8%); margin-top: 8px; } @@ -394,23 +394,28 @@ } .cancel-btn { - color: rgba(0, 0, 0, 0.6); + color: rgb(0 0 0 / 60%); height: 44px; } /* ── Responsive ── */ -@media (max-width: 960px) { +@media (width <= 960px) { .preview-grid { grid-template-columns: 1fr 1fr; } .preview-field { - &:nth-child(3n) { border-right: 1px solid rgba(0, 0, 0, 0.04); } - &:nth-child(2n) { border-right: none; } + &:nth-child(3n) { + border-right: 1px solid rgb(0 0 0 / 4%); + } + + &:nth-child(2n) { + border-right: none; + } } } -@media (max-width: 768px) { +@media (width <= 768px) { .profile-card { flex-direction: column; text-align: center; @@ -486,8 +491,13 @@ border-bottom: 1px solid #f1f5f9; transition: background 0.1s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } .fm-preview-row__sno { diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts index 1ee6a88df..6d5e3d7b0 100644 --- a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts @@ -75,7 +75,12 @@ export class ClientPreviewStepComponent { @Input() maritalStatusOptions: any[] = []; /** Address tab labels */ - addressTabs = ['permanent', 'current', 'office', 'correspondence']; + addressTabs = [ + 'permanent', + 'current', + 'office', + 'correspondence' + ]; addressTabLabels: Record = { permanent: 'Permanent Address', current: 'Current / Residential', diff --git a/src/app/clients/clients-view/address-tab/address-tab.component.html b/src/app/clients/clients-view/address-tab/address-tab.component.html index 9ac75b190..769fe665e 100644 --- a/src/app/clients/clients-view/address-tab/address-tab.component.html +++ b/src/app/clients/clients-view/address-tab/address-tab.component.html @@ -34,12 +34,18 @@ {{ address.addressType }} - + {{ address.isActive ? 'Active' : 'Inactive' }} - {{ address.city || '' }}{{ address.city && (address.countyDistrict || address.addressLine3) ? ', ' : '' }}{{ address.countyDistrict || address.addressLine3 || '' }}{{ address.postalCode ? ' - ' + address.postalCode : '' }} + {{ address.city || '' }}{{ address.city && (address.countyDistrict || address.addressLine3) ? ', ' : '' + }}{{ address.countyDistrict || address.addressLine3 || '' + }}{{ address.postalCode ? ' - ' + address.postalCode : '' }} @@ -101,12 +107,16 @@ @if (address.stateProvinceId) {
State
-
{{ getSelectedValue('stateProvinceIdOptions', address.stateProvinceId)?.name || '-' }}
+
+ {{ getSelectedValue('stateProvinceIdOptions', address.stateProvinceId)?.name || '-' }} +
}
Country
-
{{ getSelectedValue('countryIdOptions', address.countryId)?.name || 'India' }}
+
+ {{ getSelectedValue('countryIdOptions', address.countryId)?.name || 'India' }} +
diff --git a/src/app/clients/clients-view/address-tab/address-tab.component.scss b/src/app/clients/clients-view/address-tab/address-tab.component.scss index 8166ba235..65b357191 100644 --- a/src/app/clients/clients-view/address-tab/address-tab.component.scss +++ b/src/app/clients/clients-view/address-tab/address-tab.component.scss @@ -92,7 +92,7 @@ $red-light: #fce8e6; margin-bottom: 12px !important; border-radius: 10px !important; border: 1px solid $gray-200 !important; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06) !important; + box-shadow: 0 1px 3px rgb(0 0 0 / 6%) !important; &::ng-deep .mat-expansion-panel-body { padding: 0 20px 20px !important; @@ -194,11 +194,11 @@ $red-light: #fce8e6; border-radius: 8px; overflow: hidden; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: repeat(2, 1fr); } - @media (max-width: 480px) { + @media (width <= 480px) { grid-template-columns: 1fr; } } @@ -233,7 +233,7 @@ $red-light: #fce8e6; word-break: break-word; &--mono { - font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; font-size: 13px; letter-spacing: 0.5px; } diff --git a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.html b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.html index 813399750..8a428867d 100644 --- a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.html +++ b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.html @@ -24,52 +24,100 @@
- - - -
- -
- {{ 'labels.inputs.Account Number' | translate }} - - - @for (account of allAccounts; track account.accountNo) { - - {{ account.accountNo }} ({{ account.productName }}) - - } - - -
- - -
- {{ 'labels.inputs.Client ID' | translate }} - {{ clientId }} -
- - -
- {{ 'labels.inputs.Client Name' | translate }} - {{ clientName }} -
- - -
- {{ 'labels.inputs.Account Type' | translate }} - {{ selectedAccountType || '—' }} -
+ +
+
+
+ @if (ckycNumber) { + + } @else { + + }
- - +
+
+ @if (ckycNumber) { + SYNCED WITH CERSAI + } @else { + PENDING CERSAI SYNC + } +
+ @if (ckycNumber) { +
CKYC Registry Number
+
{{ ckycNumber }}
+
+ @if (ckycUploadedOn) { + + + Uploaded on {{ ckycUploadedOn }} + + } + @if (ckycBatchId) { + · + + + Batch {{ ckycBatchId }} + + } +
+ } @else { +
This client has not yet been synced to the CERSAI registry
+
+ Collect all 4 artefacts below, then create a batch from the Record Master to upload. +
+ } +
+
+ + @if (ckycNumber) { +
+ + Live +
+ } +
+ + +
+
+ CIF Number + {{ cifNumber || clientId }} +
+ +
+ Full Name + {{ clientName || '—' }} +
+ +
+ Email + @if (clientEmail) { + {{ clientEmail }} + } @else { + No email on file + } +
+ +
+ Mobile + @if (clientMobile) { + {{ clientMobile }} + } @else { + No mobile on file + } +
+

{{ 'labels.heading.CKYC Documents' | translate }}

+ @if (checkingExisting) { +
+ + Checking for already-uploaded artefacts… +
+ } +
@@ -78,64 +126,101 @@ home {{ 'labels.inputs.Address Proof Document' | translate }}
-

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG, PDF

-
- - - - - -
- - @if (uploadErrors['ADDRESS_PROOF']) { -
- error_outline - {{ uploadErrors['ADDRESS_PROOF'] }} + + @if (existing.ADDRESS_PROOF.present) { +
+
+ +
+ Already available + from {{ existing.ADDRESS_PROOF.source }} +
+
+
+ + {{ existing.ADDRESS_PROOF.name }} + @if (existing.ADDRESS_PROOF.documentKey) { + {{ existing.ADDRESS_PROOF.documentKey }} + } + @if (existing.ADDRESS_PROOF.fileCount && existing.ADDRESS_PROOF.fileCount > 0) { + + {{ existing.ADDRESS_PROOF.fileCount }} + {{ existing.ADDRESS_PROOF.fileCount === 1 ? 'file' : 'files' }} + + } +
+
} - @if (uploadResponses['ADDRESS_PROOF']) { -
-

- {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['ADDRESS_PROOF'].status }} -

-

- {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['ADDRESS_PROOF'].requestId }} -

-

- {{ 'labels.inputs.Acknowledgement Number' | translate }}: - {{ uploadResponses['ADDRESS_PROOF'].acknowledgementNumber }} -

-

- {{ 'labels.inputs.Processing Status' | translate }}: - {{ uploadResponses['ADDRESS_PROOF'].processingStatus }} -

+ + @if (!existing.ADDRESS_PROOF.present || existing.ADDRESS_PROOF.expanded) { +

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG, PDF

+
+ + + + +
+ + @if (uploadErrors['ADDRESS_PROOF']) { +
+ error_outline + {{ uploadErrors['ADDRESS_PROOF'] }} +
+ } + @if (uploadResponses['ADDRESS_PROOF']) { +
+

+ {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['ADDRESS_PROOF'].status }} +

+

+ {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['ADDRESS_PROOF'].requestId }} +

+

+ {{ 'labels.inputs.Acknowledgement Number' | translate }}: + {{ uploadResponses['ADDRESS_PROOF'].acknowledgementNumber }} +

+

+ {{ 'labels.inputs.Processing Status' | translate }}: + {{ uploadResponses['ADDRESS_PROOF'].processingStatus }} +

+
+ } } @@ -147,64 +232,100 @@ photo_camera {{ 'labels.inputs.Photo Proof Document' | translate }}
-

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG, PDF

-
- - - - - -
- - @if (uploadErrors['PHOTO_PROOF']) { -
- error_outline - {{ uploadErrors['PHOTO_PROOF'] }} + + @if (existing.PHOTO_PROOF.present) { +
+
+ +
+ Already available + from {{ existing.PHOTO_PROOF.source }} +
+
+
+ + {{ existing.PHOTO_PROOF.name }} + @if (existing.PHOTO_PROOF.documentKey) { + {{ existing.PHOTO_PROOF.documentKey }} + } + @if (existing.PHOTO_PROOF.fileCount && existing.PHOTO_PROOF.fileCount > 0) { + + {{ existing.PHOTO_PROOF.fileCount }} {{ existing.PHOTO_PROOF.fileCount === 1 ? 'file' : 'files' }} + + } +
+
} - @if (uploadResponses['PHOTO_PROOF']) { -
-

- {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['PHOTO_PROOF'].status }} -

-

- {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['PHOTO_PROOF'].requestId }} -

-

- {{ 'labels.inputs.Acknowledgement Number' | translate }}: - {{ uploadResponses['PHOTO_PROOF'].acknowledgementNumber }} -

-

- {{ 'labels.inputs.Processing Status' | translate }}: - {{ uploadResponses['PHOTO_PROOF'].processingStatus }} -

+ + @if (!existing.PHOTO_PROOF.present || existing.PHOTO_PROOF.expanded) { +

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG, PDF

+
+ + + + +
+ + @if (uploadErrors['PHOTO_PROOF']) { +
+ error_outline + {{ uploadErrors['PHOTO_PROOF'] }} +
+ } + @if (uploadResponses['PHOTO_PROOF']) { +
+

+ {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['PHOTO_PROOF'].status }} +

+

+ {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['PHOTO_PROOF'].requestId }} +

+

+ {{ 'labels.inputs.Acknowledgement Number' | translate }}: + {{ uploadResponses['PHOTO_PROOF'].acknowledgementNumber }} +

+

+ {{ 'labels.inputs.Processing Status' | translate }}: + {{ uploadResponses['PHOTO_PROOF'].processingStatus }} +

+
+ } } @@ -216,64 +337,91 @@ person {{ 'labels.inputs.Image of Client' | translate }}
-

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG

-
- - - - - -
- - @if (uploadErrors['CLIENT_IMAGE']) { -
- error_outline - {{ uploadErrors['CLIENT_IMAGE'] }} + + @if (existing.CLIENT_IMAGE.present) { +
+
+ +
+ Already available + from {{ existing.CLIENT_IMAGE.source }} +
+
+ @if (existing.CLIENT_IMAGE.previewUrl) { + Client photo + } +
} - @if (uploadResponses['CLIENT_IMAGE']) { -
-

- {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['CLIENT_IMAGE'].status }} -

-

- {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['CLIENT_IMAGE'].requestId }} -

-

- {{ 'labels.inputs.Acknowledgement Number' | translate }}: - {{ uploadResponses['CLIENT_IMAGE'].acknowledgementNumber }} -

-

- {{ 'labels.inputs.Processing Status' | translate }}: - {{ uploadResponses['CLIENT_IMAGE'].processingStatus }} -

+ + @if (!existing.CLIENT_IMAGE.present || existing.CLIENT_IMAGE.expanded) { +

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG

+
+ + + + +
+ + @if (uploadErrors['CLIENT_IMAGE']) { +
+ error_outline + {{ uploadErrors['CLIENT_IMAGE'] }} +
+ } + @if (uploadResponses['CLIENT_IMAGE']) { +
+

+ {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['CLIENT_IMAGE'].status }} +

+

+ {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['CLIENT_IMAGE'].requestId }} +

+

+ {{ 'labels.inputs.Acknowledgement Number' | translate }}: + {{ uploadResponses['CLIENT_IMAGE'].acknowledgementNumber }} +

+

+ {{ 'labels.inputs.Processing Status' | translate }}: + {{ uploadResponses['CLIENT_IMAGE'].processingStatus }} +

+
+ } } @@ -285,64 +433,100 @@ draw {{ 'labels.inputs.Signature of Client' | translate }}
-

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG

-
- - - - - -
- - @if (uploadErrors['CLIENT_SIGNATURE']) { -
- error_outline - {{ uploadErrors['CLIENT_SIGNATURE'] }} + + @if (existing.CLIENT_SIGNATURE.present) { +
+
+ +
+ Already available + from {{ existing.CLIENT_SIGNATURE.source }} +
+
+ @if (existing.CLIENT_SIGNATURE.previewUrl) { + Client signature + } +
+ + {{ existing.CLIENT_SIGNATURE.name }} +
+
} - @if (uploadResponses['CLIENT_SIGNATURE']) { -
-

- {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['CLIENT_SIGNATURE'].status }} -

-

- {{ 'labels.inputs.Request ID' | translate }}: {{ uploadResponses['CLIENT_SIGNATURE'].requestId }} -

-

- {{ 'labels.inputs.Acknowledgement Number' | translate }}: - {{ uploadResponses['CLIENT_SIGNATURE'].acknowledgementNumber }} -

-

- {{ 'labels.inputs.Processing Status' | translate }}: - {{ uploadResponses['CLIENT_SIGNATURE'].processingStatus }} -

+ + @if (!existing.CLIENT_SIGNATURE.present || existing.CLIENT_SIGNATURE.expanded) { +

{{ 'labels.inputs.Accepted formats' | translate }}: PNG, JPEG, JPG

+
+ + + + +
+ + @if (uploadErrors['CLIENT_SIGNATURE']) { +
+ error_outline + {{ uploadErrors['CLIENT_SIGNATURE'] }} +
+ } + @if (uploadResponses['CLIENT_SIGNATURE']) { +
+

+ {{ 'labels.inputs.Status' | translate }}: {{ uploadResponses['CLIENT_SIGNATURE'].status }} +

+

+ {{ 'labels.inputs.Request ID' | translate }}: + {{ uploadResponses['CLIENT_SIGNATURE'].requestId }} +

+

+ {{ 'labels.inputs.Acknowledgement Number' | translate }}: + {{ uploadResponses['CLIENT_SIGNATURE'].acknowledgementNumber }} +

+

+ {{ 'labels.inputs.Processing Status' | translate }}: + {{ uploadResponses['CLIENT_SIGNATURE'].processingStatus }} +

+
+ } } diff --git a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.scss b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.scss index 1b5d6650d..ba29cd983 100644 --- a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.scss +++ b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.scss @@ -49,35 +49,266 @@ line-height: 18px; } -.ckyc-info-card { - margin-bottom: 24px; +/* ===================== CERSAI Sync Hero ===================== */ +.cersai-hero { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + padding: 22px 26px; + border-radius: 16px; + overflow: hidden; + margin-bottom: 18px; + color: #fff; + box-shadow: 0 6px 22px rgb(0 0 0 / 10%); + + &::before { + content: ''; + position: absolute; + top: -60%; + right: -10%; + width: 320px; + height: 320px; + border-radius: 50%; + background: radial-gradient(circle, rgb(255 255 255 / 18%), transparent 70%); + pointer-events: none; + } + + &--synced { + background: linear-gradient(135deg, #0f6e56 0%, #1d9e75 55%, #2fb88c 100%); + } + + &--pending { + background: linear-gradient(135deg, #8a5a14 0%, #ba7517 55%, #d4892f 100%); + } + + &__left { + display: flex; + align-items: center; + gap: 18px; + min-width: 0; + position: relative; + flex: 1; + } + + &__icon { + width: 60px; + height: 60px; + border-radius: 16px; + background: rgb(255 255 255 / 20%); + border: 1px solid rgb(255 255 255 / 35%); + display: flex; + align-items: center; + justify-content: center; + font-size: 26px; + flex-shrink: 0; + backdrop-filter: blur(10px); + } + + &__text { + min-width: 0; + flex: 1; + } + + &__tag { + display: inline-block; + padding: 4px 12px; + border-radius: 20px; + background: rgb(255 255 255 / 22%); + border: 1px solid rgb(255 255 255 / 40%); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.8px; + text-transform: uppercase; + backdrop-filter: blur(8px); + } + + &__ckyc-label { + font-size: 11px; + font-weight: 600; + opacity: 0.9; + text-transform: uppercase; + letter-spacing: 0.5px; + margin: 10px 0 4px; + } + + &__ckyc-number { + font-size: 24px; + font-weight: 800; + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; + letter-spacing: 0.5px; + line-height: 1.1; + margin-bottom: 8px; + } + + &__meta { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + opacity: 0.95; + flex-wrap: wrap; + + fa-icon { + font-size: 11px; + opacity: 0.9; + margin-right: 3px; + } + + strong { + font-weight: 700; + } + } + + &__sep { + opacity: 0.5; + } + + &__hint { + font-size: 12px; + opacity: 0.9; + line-height: 1.5; + max-width: 480px; + margin-top: 4px; + } + + &__pulse { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 7px 14px; + background: rgb(255 255 255 / 20%); + border: 1px solid rgb(255 255 255 / 35%); + border-radius: 20px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + backdrop-filter: blur(8px); + flex-shrink: 0; + position: relative; + z-index: 1; + } + + &__pulse-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #9fe1cb; + box-shadow: 0 0 0 0 rgb(159 225 203 / 70%); + animation: cersai-pulse 2s infinite; + } } +@keyframes cersai-pulse { + 0% { + box-shadow: 0 0 0 0 rgb(159 225 203 / 70%); + } + + 70% { + box-shadow: 0 0 0 10px rgb(159 225 203 / 0%); + } + + 100% { + box-shadow: 0 0 0 0 rgb(159 225 203 / 0%); + } +} + +/* ===================== Client info grid ===================== */ .ckyc-info-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 16px; - align-items: start; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; + margin-bottom: 22px; } .ckyc-info-item { display: flex; flex-direction: column; - gap: 4px; + gap: 6px; + padding: 14px 16px; + background: #fff; + border: 1px solid #e8eaed; + border-radius: 12px; + min-width: 0; + transition: all 0.15s; + + &:hover { + border-color: #c6dafc; + box-shadow: 0 2px 8px rgb(26 115 232 / 6%); + } +} + +@media (width <= 900px) { + .ckyc-info-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .cersai-hero { + flex-direction: column; + align-items: flex-start; + + &__left { + width: 100%; + } + + &__icon { + width: 52px; + height: 52px; + font-size: 22px; + } + + &__ckyc-number { + font-size: 20px; + } + } +} + +@media (width <= 500px) { + .ckyc-info-grid { + grid-template-columns: 1fr; + } } .ckyc-info-label { - font-size: 11px; - font-weight: 600; - color: #888; + font-size: 10px; + font-weight: 700; + color: #5f6368; text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.06em; + display: inline-flex; + align-items: center; + gap: 6px; + + fa-icon { + color: #1a73e8; + font-size: 11px; + } } .ckyc-info-value { - font-size: 14px; - color: #333; - min-height: 20px; + font-size: 13px; + color: #202124; + font-weight: 600; + min-height: 18px; + word-break: break-word; + + &--mono { + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; + font-size: 12px; + color: #1a73e8; + letter-spacing: 0.3px; + } + + &--missing { + color: #9aa0a6; + font-weight: 500; + font-style: italic; + } +} + +.ckyc-info-hint { + display: none; } .ckyc-section-title { @@ -87,31 +318,60 @@ .ckyc-upload-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); + grid-template-columns: repeat(2, 1fr); gap: 16px; margin-bottom: 16px; + align-items: stretch; } .ckyc-upload-card { + border-radius: 12px !important; + border: 1px solid #e8eaed; + box-shadow: none !important; + transition: + border-color 0.15s, + box-shadow 0.15s; + display: flex; + flex-direction: column; + min-height: 240px; + + &:hover { + border-color: #c6dafc; + box-shadow: 0 2px 10px rgb(26 115 232 / 6%) !important; + } + .mat-mdc-card-content { - padding: 16px; + padding: 18px 20px !important; + display: flex; + flex-direction: column; + height: 100%; } } .ckyc-upload-header { display: flex; align-items: center; - gap: 8px; + gap: 10px; font-weight: 600; font-size: 14px; - margin-bottom: 8px; + color: #202124; + padding-bottom: 12px; + margin-bottom: 0; + border-bottom: 1px solid #f1f3f4; } .ckyc-upload-icon { - font-size: 20px; - height: 20px; - width: 20px; - color: #1976d2; + font-size: 18px; + height: 32px; + width: 32px; + color: #fff; + background: linear-gradient(135deg, #1a73e8, #4285f4); + border-radius: 8px; + display: flex !important; + align-items: center; + justify-content: center; + box-shadow: 0 2px 6px rgb(26 115 232 / 25%); + flex-shrink: 0; } .ckyc-upload-hint { @@ -163,6 +423,12 @@ } } +@media (width <= 900px) { + .ckyc-upload-grid { + grid-template-columns: 1fr; + } +} + @media (width <= 600px) { .ckyc-info-grid { grid-template-columns: 1fr; @@ -180,3 +446,157 @@ width: 100%; } } + +/* ===================== Already-available banners ===================== */ + +.ckyc-existing-loading { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 8px 14px; + background: #e8f0fe; + color: #0c447c; + border-radius: 8px; + font-size: 12px; + margin-bottom: 14px; + + mat-progress-spinner { + display: inline-block; + } +} + +.ckyc-existing { + display: flex; + flex-direction: column; + gap: 12px; + padding: 14px 14px 12px; + background: #f0fbf6; + border: 1px solid #cfecdd; + border-radius: 10px; + margin-top: 14px; + flex: 1; + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0 auto 0 0; + width: 3px; + background: #1d9e75; + border-radius: 10px 0 0 10px; + } + + &__head { + display: flex; + align-items: center; + gap: 10px; + + strong { + display: block; + font-size: 13px; + font-weight: 700; + color: #085041; + line-height: 1.2; + } + } + + &__ok { + font-size: 20px; + color: #1d9e75; + flex-shrink: 0; + } + + &__source { + display: block; + font-size: 11px; + color: #0f6e56; + margin-top: 2px; + font-weight: 500; + } + + &__meta { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: #085041; + flex-wrap: wrap; + padding: 8px 10px; + background: #fff; + border: 1px solid #cfecdd; + border-radius: 8px; + + > span:first-of-type { + font-weight: 600; + } + + fa-icon { + color: #1d9e75; + font-size: 12px; + } + } + + &__key { + font-family: 'SF Mono', Consolas, monospace; + font-size: 11px; + background: #e1f5ee; + color: #0f6e56; + padding: 2px 8px; + border-radius: 10px; + border: 1px solid #cfecdd; + font-weight: 600; + } + + &__count { + font-size: 10px; + font-weight: 700; + background: #1d9e75; + color: #fff; + padding: 2px 8px; + border-radius: 10px; + text-transform: uppercase; + letter-spacing: 0.3px; + } + + &__thumb { + width: 100px; + height: 100px; + border-radius: 10px; + border: 2px solid #fff; + outline: 1px solid #cfecdd; + background: #fff; + object-fit: cover; + align-self: center; + box-shadow: 0 2px 8px rgb(0 0 0 / 6%); + + &--wide { + width: 200px; + height: 80px; + object-fit: contain; + } + } + + &__replace { + align-self: stretch; + margin-top: auto; + height: 34px !important; + font-size: 11px !important; + font-weight: 600 !important; + border-radius: 8px !important; + border: 1px solid #cfecdd !important; + color: #0f6e56 !important; + background: #fff !important; + padding: 0 12px !important; + letter-spacing: 0.2px; + + fa-icon { + margin-right: 6px; + font-size: 11px; + } + + &:hover { + background: #e1f5ee !important; + border-color: #1d9e75 !important; + } + } +} diff --git a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.ts b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.ts index 5f66d01bc..26a2c6b17 100644 --- a/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.ts +++ b/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.ts @@ -9,7 +9,7 @@ /** Angular Imports */ import { Component, OnInit, OnDestroy, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Subject, takeUntil, catchError, of } from 'rxjs'; +import { Subject, takeUntil, catchError, of, forkJoin } from 'rxjs'; /** Custom Services */ import { ClientsService } from '../../clients.service'; @@ -19,6 +19,17 @@ import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +interface ExistingArtefact { + present: boolean; + name?: string; + source?: string; + previewUrl?: string; + documentId?: string; + documentKey?: string; + fileCount?: number; + expanded?: boolean; +} + @Component({ selector: 'mifosx-ckyc-tab', templateUrl: './ckyc-tab.component.html', @@ -41,6 +52,10 @@ export class CkycTabComponent implements OnInit, OnDestroy { /** Top section - read-only client info */ clientId: string; clientName: string = ''; + clientEmail: string = ''; + clientMobile: string = ''; + cifNumber: string = ''; + ckycNumber: string = ''; allAccounts: any[] = []; selectedAccount: any = null; selectedAccountType: string = ''; @@ -48,6 +63,17 @@ export class CkycTabComponent implements OnInit, OnDestroy { /** State */ loading = true; ckycData: any = null; + ckycUploadedOn = ''; + ckycBatchId = ''; + checkingExisting = false; + + /** Pre-existing artefacts detected from client data */ + existing: Record<'ADDRESS_PROOF' | 'PHOTO_PROOF' | 'CLIENT_IMAGE' | 'CLIENT_SIGNATURE', ExistingArtefact> = { + ADDRESS_PROOF: { present: false }, + PHOTO_PROOF: { present: false }, + CLIENT_IMAGE: { present: false }, + CLIENT_SIGNATURE: { present: false } + }; /** File references */ addressProofFile: File | null = null; @@ -80,13 +106,18 @@ export class CkycTabComponent implements OnInit, OnDestroy { this.loadClientInfo(); this.loadAccounts(); this.loadCkycData(); + this.loadExistingArtefacts(); }); } private loadClientInfo(): void { this.route.parent.data.subscribe((data: any) => { - if (data?.clientViewData) { - this.clientName = data.clientViewData.displayName || ''; + const cv = data?.clientViewData; + if (cv) { + this.clientName = cv.displayName || ''; + this.clientEmail = cv.emailAddress || ''; + this.clientMobile = cv.mobileNo || ''; + this.cifNumber = cv.accountNo || cv.externalId || ''; } }); } @@ -122,8 +153,10 @@ export class CkycTabComponent implements OnInit, OnDestroy { private loadCkycData(): void { this.loading = true; + // Read CKYC registry data from /clients/{id}/kyc — that's where the + // SFTP upload flow persists ckycNumber / ckycUploadedOn / ckycBatchId. this.clientsService - .getClientCkyc(this.clientId) + .getClientKyc(this.clientId) .pipe( takeUntil(this.destroy$), catchError(() => { @@ -134,9 +167,120 @@ export class CkycTabComponent implements OnInit, OnDestroy { .subscribe((data: any) => { this.loading = false; this.ckycData = data; + this.ckycNumber = data?.ckycNumber || ''; + this.ckycUploadedOn = this.formatJavaDate(data?.ckycUploadedOn); + this.ckycBatchId = data?.ckycBatchId || ''; }); } + private formatJavaDate(value: any): string { + if (!value) return ''; + if (Array.isArray(value) && value.length >= 3) { + const [ + y, + m, + d + ] = value; + return `${String(d).padStart(2, '0')}-${String(m).padStart(2, '0')}-${y}`; + } + if (typeof value === 'string') return value; + return ''; + } + + /** + * Fetch photo (profile image), signature (client document), + * ID proof and address proof (client identifiers) so already-uploaded + * artefacts are shown here instead of forcing re-upload. + */ + private loadExistingArtefacts(): void { + this.checkingExisting = true; + + forkJoin({ + photo: this.clientsService.getClientProfileImage(this.clientId).pipe(catchError(() => of(null))), + documents: this.clientsService.getClientDocuments(this.clientId).pipe(catchError(() => of([]))), + identifiers: this.clientsService.getClientIdentifiers(this.clientId).pipe(catchError(() => of([]))), + proofTypes: this.clientsService.getKycProofTypes().pipe(catchError(() => of({ idProofs: [], addressProofs: [] }))) + }) + .pipe(takeUntil(this.destroy$)) + .subscribe(({ photo, documents, identifiers, proofTypes }) => { + this.checkingExisting = false; + + // 1. Photo / image of client — from m_client.image_id + if (photo && typeof photo === 'string' && photo.length > 0) { + this.existing.CLIENT_IMAGE = { + present: true, + source: 'Client registration', + previewUrl: photo, + name: 'Profile photo' + }; + } + + // 2. Signature — client document with name='clientSignature' + const signatureDoc = (documents as any[])?.find((d: any) => d?.name === 'clientSignature'); + if (signatureDoc) { + this.existing.CLIENT_SIGNATURE = { + present: true, + source: 'Client registration', + name: signatureDoc.fileName || 'Client signature', + documentId: signatureDoc.id + }; + // Fetch blob for preview + this.clientsService + .getClientSignatureImage(this.clientId, signatureDoc.id) + .pipe(takeUntil(this.destroy$)) + .subscribe({ + next: (blob: any) => { + if (blob) { + this.existing.CLIENT_SIGNATURE.previewUrl = URL.createObjectURL(blob); + } + }, + error: () => undefined + }); + } + + // 3 & 4. ID proof + Address proof — client identifiers classified via proof-types + const idProofIds = new Set((proofTypes?.idProofs || []).map((p: any) => String(p.id))); + const addressProofIds = new Set((proofTypes?.addressProofs || []).map((p: any) => String(p.id))); + + const idList = Array.isArray(identifiers) ? identifiers : (identifiers as any)?.pageItems || []; + + for (const identity of idList) { + const docTypeId = String(identity?.documentType?.id ?? ''); + const isActive = + !identity?.status || + identity?.status?.code === 'clientIdentifierStatusType.active' || + identity?.status === 'clientIdentifierStatusType.active' || + identity?.status === 'Active'; + if (!isActive) continue; + + const proofName = identity?.documentType?.name || 'Identifier'; + // Address proof documentKey is auto-generated as " - " + // in identities-tab; hide it when it's just that synthetic key. + const rawKey = identity?.documentKey || ''; + const isSyntheticKey = rawKey.startsWith(`${proofName} - `); + const common = { + present: true, + source: 'Identities section', + name: proofName, + documentKey: isSyntheticKey ? '' : rawKey, + documentId: identity?.id, + fileCount: (identity?.documents || []).length + }; + + if (idProofIds.has(docTypeId) && !this.existing.PHOTO_PROOF.present) { + this.existing.PHOTO_PROOF = common; + } else if (addressProofIds.has(docTypeId) && !this.existing.ADDRESS_PROOF.present) { + this.existing.ADDRESS_PROOF = common; + } + } + }); + } + + toggleReplace(documentType: string): void { + const entry = (this.existing as any)[documentType]; + if (entry) entry.expanded = !entry.expanded; + } + onAccountSelect(accountNo: string): void { this.selectedAccount = this.allAccounts.find((a) => a.accountNo === accountNo) || null; this.selectedAccountType = this.selectedAccount?.accountType || ''; @@ -215,5 +359,9 @@ export class CkycTabComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); + // Release object URLs + Object.values(this.existing).forEach((e) => { + if (e.previewUrl?.startsWith('blob:')) URL.revokeObjectURL(e.previewUrl); + }); } } diff --git a/src/app/clients/clients-view/clients-view.component.html b/src/app/clients/clients-view/clients-view.component.html index 182f7657e..969075cf5 100644 --- a/src/app/clients/clients-view/clients-view.component.html +++ b/src/app/clients/clients-view/clients-view.component.html @@ -10,29 +10,36 @@
- +
- - -
- {{ 'labels.inputs.View Signature' | translate }} + {{ + 'labels.inputs.View Signature' | translate + }}
@@ -40,12 +47,19 @@
- + + [entityName]=" + complianceHideClientData ? maskName(clientViewData.displayName) : clientViewData.displayName + " + [display]="'right'" + [hideCopy]="complianceHideClientData" + > {{ 'labels.status.' + clientViewData.status.value | translate }} @@ -77,8 +91,12 @@ {{ 'labels.inputs.External Id' | translate }} + [externalId]=" + complianceHideClientData ? maskValue(clientViewData.externalId) : clientViewData.externalId + " + [completed]="true" + [hideCopy]="complianceHideClientData" + >
} @@ -118,7 +136,9 @@
+ [completed]="true" + [hideCopy]="complianceHideClientData" + >
} @@ -127,8 +147,12 @@
{{ 'labels.inputs.Email' | translate }}
+ [externalId]=" + complianceHideClientData ? maskEmail(clientViewData.emailAddress) : clientViewData.emailAddress + " + [completed]="true" + [hideCopy]="complianceHideClientData" + >
} @@ -148,10 +172,26 @@
DIP Status
- - {{ (clientViewData.dipVerified === true || clientViewData.dipVerified === 'true' || clientViewData.dipVerified === 1) ? 'Verified' : 'Pending' }} + + {{ + clientViewData.dipVerified === true || + clientViewData.dipVerified === 'true' || + clientViewData.dipVerified === 1 + ? 'Verified' + : 'Pending' + }}
@@ -160,10 +200,26 @@
Sanctions Screening
- - {{ (clientViewData.nameVerified === true || clientViewData.nameVerified === 'true' || clientViewData.nameVerified === 1) ? 'Clear' : 'Pending' }} + + {{ + clientViewData.nameVerified === true || + clientViewData.nameVerified === 'true' || + clientViewData.nameVerified === 1 + ? 'Clear' + : 'Pending' + }}
@@ -206,244 +262,299 @@
- - + + @if (isActive()) { + + } + + + + @if (!clientViewData.staffId) { + + } + + @if (clientViewData.staffId) { + + + + } - @if (isActive()) { - - } + + - + + + + + + + + - @if (!clientViewData.staffId) { - - } + + + + @if (clientViewData.status.value === 'Pending') { + + } + @if (clientViewData.status.value === 'Pending') { + + } + @if (clientViewData.status.value === 'Pending') { + + } + @if (clientViewData.status.value === 'Pending') { + + } + @if (clientViewData.status.value === 'Closed') { + + } + @if (clientViewData.status.value === 'Rejected') { + + } + @if (clientViewData.status.value === 'Transfer in progress') { + + } + @if (clientViewData.status.value === 'Transfer in progress') { + + } + @if (clientViewData.status.value === 'Transfer in progress') { + + } + - @if (clientViewData.staffId) { - - - - } - - - - - - + + + + + + + + + + @if (clientViewData.status.value !== 'Transfer on hold') { + - - - - - - - - - - @if (clientViewData.status.value === 'Pending') { - - } - @if (clientViewData.status.value === 'Pending') { - - } - @if (clientViewData.status.value === 'Pending') { - - } - @if (clientViewData.status.value === 'Pending') { - - } - @if (clientViewData.status.value === 'Closed') { - - } - @if (clientViewData.status.value === 'Rejected') { - - } - @if (clientViewData.status.value === 'Transfer in progress') { - - } - @if (clientViewData.status.value === 'Transfer in progress') { - - } - @if (clientViewData.status.value === 'Transfer in progress') { - - } - - - - - - - - - - - - @if (clientViewData.status.value !== 'Transfer on hold') { - - - - } - + + } +
@@ -35,30 +41,38 @@
{{ identity.documentType?.name }} - + {{ identity.status === 'clientIdentifierStatusType.active' ? 'Active' : 'Inactive' }}
-
-
-
-
{{ 'labels.inputs.Id' | translate }}
-
{{ identity.id }}
-
-
-
{{ 'labels.inputs.Document Key' | translate }}
-
{{ identity.documentKey }}
+
+
+ + {{ 'labels.inputs.Document Key' | translate }} + {{ identity.documentKey || '—' }}
@if (identity.description) { -
-
{{ 'labels.inputs.Description' | translate }}
-
{{ identity.description }}
+
+ + {{ 'labels.inputs.Description' | translate }} + {{ identity.description }}
}
@@ -68,25 +82,35 @@
{{ 'labels.inputs.Identity Documents' | translate }} + {{ identity.documents.length }}
@for (document of identity.documents; track trackByDocumentId($index, document)) { -
+ tabindex="0" + role="button" + [attr.aria-label]="('labels.buttons.Preview' | translate) + ': ' + document.name" + > @if (previewThumbnails[document.id]) { - + } @else {
- +
}
{{ document.name }} @if (document.fileName) { - {{ document.fileName }} + {{ document.fileName }} }
@if (isPreviewable(document)) { diff --git a/src/app/clients/clients-view/identities-tab/identities-tab.component.scss b/src/app/clients/clients-view/identities-tab/identities-tab.component.scss index 88cbb0595..b90577cd5 100644 --- a/src/app/clients/clients-view/identities-tab/identities-tab.component.scss +++ b/src/app/clients/clients-view/identities-tab/identities-tab.component.scss @@ -37,7 +37,10 @@ $red-light: #fce8e6; gap: 10px; } - &__icon { font-size: 16px; color: $primary; } + &__icon { + font-size: 16px; + color: $primary; + } &__title { margin: 0; @@ -71,198 +74,318 @@ $red-light: #fce8e6; border-radius: 10px; border: 1px dashed $gray-200; - &__icon { font-size: 32px; color: $gray-500; margin-bottom: 8px; } - &__text { color: $gray-500; font-size: 13px; margin: 0; } + &__icon { + font-size: 32px; + color: $gray-500; + margin-bottom: 8px; + } + + &__text { + color: $gray-500; + font-size: 13px; + margin: 0; + } } // Identity Card .idt-card { background: $white; border: 1px solid $gray-200; - border-radius: 8px; + border-radius: 10px; overflow: hidden; - margin-bottom: 12px; + margin-bottom: 14px; + transition: + box-shadow 0.15s, + border-color 0.15s; + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0 auto 0 0; + width: 3px; + background: $primary; + opacity: 0.7; + } + + &:hover { + border-color: $primary-light; + box-shadow: 0 2px 8px rgb(26 115 232 / 8%); + } &__header { display: flex; align-items: center; justify-content: space-between; - padding: 12px 18px; - background: $gray-50; - border-bottom: 1px solid $gray-200; + padding: 14px 18px 14px 22px; + background: linear-gradient(to right, $primary-light 0%, $gray-50 40%, $white 100%); + border-bottom: 1px solid $gray-100; } &__title { display: flex; align-items: center; - gap: 10px; + gap: 12px; + min-width: 0; + flex: 1; } &__type-icon { - font-size: 14px; - color: $primary; - width: 28px; - height: 28px; + font-size: 15px; + color: $white; + width: 34px; + height: 34px; display: flex; align-items: center; justify-content: center; - background: $primary-light; - border-radius: 6px; + background: linear-gradient(135deg, $primary, #4285f4); + border-radius: 8px; + box-shadow: 0 2px 6px rgb(26 115 232 / 25%); + flex-shrink: 0; } &__type { - font-size: 14px; + font-size: 15px; font-weight: 600; color: $gray-900; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } &__status { font-size: 10px; - font-weight: 600; - padding: 2px 9px; - border-radius: 10px; + font-weight: 700; + padding: 3px 10px; + border-radius: 12px; + text-transform: uppercase; + letter-spacing: 0.4px; + display: inline-flex; + align-items: center; + gap: 4px; - &--active { background: $green-light; color: $green; } - &--inactive { background: $red-light; color: $red; } + &::before { + content: ''; + width: 6px; + height: 6px; + border-radius: 50%; + display: inline-block; + } + + &--active { + background: $green-light; + color: $green; + + &::before { + background: $green; + } + } + + &--inactive { + background: $red-light; + color: $red; + + &::before { + background: $red; + } + } } &__delete { - width: 30px !important; - height: 30px !important; - min-width: 30px !important; + width: 32px !important; + height: 32px !important; + min-width: 32px !important; padding: 0 !important; - border-radius: 6px !important; + border-radius: 8px !important; border-color: $gray-200 !important; color: $gray-500 !important; + background: $white !important; - &:hover { color: $red !important; border-color: $red !important; background: $red-light !important; } + &:hover { + color: $red !important; + border-color: $red !important; + background: $red-light !important; + } - i { font-size: 12px; } + i { + font-size: 13px; + } } &__body { - padding: 14px 18px; + padding: 16px 18px 18px 22px; } } -// Details grid -.idt-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 0; - border: 1px solid $gray-100; - border-radius: 6px; - overflow: hidden; +// Metadata rows (compact, no grid) +.idt-meta { + display: flex; + flex-direction: column; + gap: 2px; + background: $gray-50; + border-radius: 8px; + padding: 4px 0; + margin-bottom: 14px; - @media (max-width: 600px) { grid-template-columns: 1fr; } -} + &__row { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 14px; + border-bottom: 1px dashed $gray-200; -.idt-field { - padding: 8px 12px; - border-bottom: 1px solid $gray-100; - border-right: 1px solid $gray-100; + &:last-child { + border-bottom: none; + } + } - &:nth-child(3n) { border-right: none; } - &:hover { background: $gray-50; } + &__icon { + width: 22px; + height: 22px; + display: inline-flex; + align-items: center; + justify-content: center; + color: $primary; + font-size: 12px; + background: $primary-light; + border-radius: 5px; + flex-shrink: 0; + } &__label { - font-size: 9px; - font-weight: 600; + font-size: 10px; + font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: $gray-500; - margin-bottom: 2px; + min-width: 95px; } &__value { font-size: 13px; font-weight: 500; color: $gray-900; + word-break: break-word; + flex: 1; + min-width: 0; &--mono { - font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; font-size: 12px; letter-spacing: 0.3px; + color: $primary; + font-weight: 600; + } + } + + @media (width <= 600px) { + &__row { + flex-wrap: wrap; + } + + &__label { + min-width: 0; } } } // Documents section .idt-docs { - margin-top: 12px; + margin-top: 4px; &__title { font-size: 10px; - font-weight: 600; + font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: $gray-500; - margin-bottom: 8px; + margin-bottom: 10px; + display: flex; + align-items: center; + gap: 6px; - i { color: $primary; margin-right: 4px; } + i { + color: $primary; + } + } + + &__count { + background: $primary-light; + color: $primary; + font-size: 10px; + font-weight: 700; + padding: 1px 7px; + border-radius: 8px; + margin-left: 2px; } &__grid { - display: flex; - gap: 8px; - flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 10px; } } .idt-doc { display: flex; align-items: center; - gap: 10px; - padding: 8px 12px; - background: $gray-50; - border: 1px solid $gray-100; - border-radius: 6px; - transition: all 0.12s; - min-width: 180px; + gap: 12px; + padding: 10px 12px; + background: $white; + border: 1px solid $gray-200; + border-radius: 8px; + transition: all 0.15s; + position: relative; + overflow: hidden; &--clickable { cursor: pointer; &:hover { border-color: $primary; - background: $primary-light; + box-shadow: 0 2px 8px rgb(26 115 232 / 10%); + transform: translateY(-1px); } } &__thumb { - width: 36px; - height: 36px; - border-radius: 4px; + width: 42px; + height: 42px; + border-radius: 6px; object-fit: cover; border: 1px solid $gray-200; + flex-shrink: 0; } &__placeholder { - width: 36px; - height: 36px; + width: 42px; + height: 42px; display: flex; align-items: center; justify-content: center; - background: $gray-200; - border-radius: 4px; - color: $gray-500; - font-size: 14px; + background: linear-gradient(135deg, $primary-light, $gray-100); + border: 1px solid $gray-200; + border-radius: 6px; + color: $primary; + font-size: 16px; + flex-shrink: 0; } &__info { display: flex; flex-direction: column; + gap: 2px; min-width: 0; + flex: 1; } &__name { - font-size: 12px; + font-size: 13px; font-weight: 600; color: $gray-900; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - max-width: 150px; } &__file { @@ -271,18 +394,36 @@ $red-light: #fce8e6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - max-width: 150px; + display: inline-flex; + align-items: center; + gap: 4px; + + i { + font-size: 9px; + } } &__preview { - font-size: 12px; + font-size: 13px; color: $primary; margin-left: auto; + width: 26px; + height: 26px; + display: flex; + align-items: center; + justify-content: center; + background: $primary-light; + border-radius: 6px; opacity: 0; transition: opacity 0.15s; + flex-shrink: 0; } - &:hover .idt-doc__preview { opacity: 1; } + &:hover .idt-doc__preview { + opacity: 1; + } } -.document-lightbox-host { display: none; } +.document-lightbox-host { + display: none; +} diff --git a/src/app/clients/clients-view/identities-tab/identities-tab.component.ts b/src/app/clients/clients-view/identities-tab/identities-tab.component.ts index a67ca224d..80f69613d 100644 --- a/src/app/clients/clients-view/identities-tab/identities-tab.component.ts +++ b/src/app/clients/clients-view/identities-tab/identities-tab.component.ts @@ -86,6 +86,10 @@ export class IdentitiesTabComponent implements OnDestroy { clientIdentifierTemplate: any; /** Client Id */ clientId: string; + /** Client display name (prefilled into dialog) */ + clientName = ''; + /** Formatted permanent address (prefilled into dialog) */ + clientAddress = ''; /** Identities Columns */ identitiesColumns: string[] = [ 'id', @@ -126,6 +130,36 @@ export class IdentitiesTabComponent implements OnDestroy { this.clientIdentifierTemplate = data.clientIdentifierTemplate; this.prefetchThumbnails(); }); + + // Pull display name from parent route's clientViewData + this.route.parent.data.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: any) => { + this.clientName = data?.clientViewData?.displayName || ''; + }); + + // Pull permanent address (falls back to first available address) + this.clientService.getClientAddressData(this.clientId).subscribe({ + next: (addresses: any[]) => { + if (!addresses?.length) return; + const permanent = addresses.find((a) => /permanent/i.test(a?.addressType || '')) || addresses[0]; + this.clientAddress = this.formatAddress(permanent); + }, + error: () => undefined + }); + } + + private formatAddress(addr: any): string { + if (!addr) return ''; + return [ + addr.addressLine1, + addr.addressLine2, + addr.townVillage, + addr.city, + addr.stateName, + addr.countryName, + addr.postalCode + ] + .filter((p) => !!p && String(p).trim()) + .join(', '); } ngOnDestroy(): void { @@ -143,97 +177,94 @@ export class IdentitiesTabComponent implements OnDestroy { } /** - * Add Client Identifier with unified form (identifier + document upload) + * Add Client Identifier — opens KYC dialog that may create up to two + * identifiers (identity proof + address proof), each with front/back docs. */ addIdentifier() { - // Translate document type names - const translatedDocTypes = this.clientIdentifierTemplate.allowedDocumentTypes.map((docType: any) => ({ - ...docType, - name: this.translateService.instant(`labels.catalogs.${docType.name}`) - })); - - const statusOptions = [ - { label: this.translateService.instant('labels.catalogs.Active'), value: 'Active' }, - { label: this.translateService.instant('labels.catalogs.Inactive'), value: 'Inactive' } - ]; - const dialogRef = this.dialog.open(UploadDocumentDialogComponent, { - width: '700px', + width: '880px', + maxWidth: '95vw', data: { documentIdentifier: true, - allowedDocumentTypes: translatedDocTypes, - statusOptions: statusOptions + clientName: this.clientName, + clientAddress: this.clientAddress } }); dialogRef.afterClosed().subscribe((response: any) => { - if (response) { - // Create identifier data - const identifierData = { - documentTypeId: response.documentTypeId, - status: response.status, - documentKey: response.documentKey, - description: response.description + if (!response) return; + const description = response.description || ''; + if (response.id) { + this.createIdentifierWithDocs(response.id, description, 'id'); + } + if (response.address) { + this.createIdentifierWithDocs(response.address, description, 'address'); + } + }); + } + + /** + * Create one identifier from a KYC section payload, then upload each file + * as a document attached to that identifier. + */ + private createIdentifierWithDocs(section: any, description: string, kind: 'id' | 'address'): void { + const identifierData = { + documentTypeId: section.proofId, + status: 'Active', + documentKey: kind === 'id' ? section.documentKey : `${section.proof?.name || 'Address Proof'} - ${this.clientId}`, + description: kind === 'address' && section.address ? section.address : description + }; + + this.clientService.addClientIdentifier(this.clientId, identifierData).subscribe({ + next: (res: any) => { + const newIdentifierId = res.resourceId; + const newIdentity: any = { + id: newIdentifierId, + description, + documentType: { id: section.proof?.id, name: section.proof?.name }, + documentKey: identifierData.documentKey, + documents: [] as any[], + clientId: this.clientId, + status: 'clientIdentifierStatusType.active' }; - // First create the identifier - this.clientService.addClientIdentifier(this.clientId, identifierData).subscribe({ - next: (res: any) => { - const newIdentifierId = res.resourceId; - const selectedDocType = this.clientIdentifierTemplate.allowedDocumentTypes.find( - (doc: any) => doc.id === response.documentTypeId - ); + const files: { slot: string; file: File }[] = section.files || []; + if (!files.length) { + this.clientIdentities.push(newIdentity); + return; + } - // Create new identity entry - const newIdentity: any = { - id: newIdentifierId, - description: response.description, - documentType: selectedDocType, - documentKey: response.documentKey, - documents: [] as any[], - clientId: this.clientId, - status: - response.status === 'Active' - ? 'clientIdentifierStatusType.active' - : 'clientIdentifierStatusType.inactive' - }; - - // If file was uploaded, attach document to the identifier - if (response.file) { - const formData: FormData = new FormData(); - formData.append('name', response.fileName); - formData.append('file', response.file); - this.clientService.uploadClientIdentifierDocument(newIdentifierId, formData).subscribe({ - next: (docRes: any) => { - const newDoc = { - id: docRes.resourceId, - parentEntityType: 'client_identifiers', - parentEntityId: newIdentifierId, - name: response.fileName, - fileName: response.file.name - }; - newIdentity.documents.push(newDoc); - this.clientIdentities.push(newIdentity); - // Array mutation triggers @for re-render - this.setThumbnail(newDoc); - }, - error: (err: any) => { - console.error('Failed to upload document', err); - // Still add the identifier even if document upload fails - this.clientIdentities.push(newIdentity); - // Array mutation triggers @for re-render - } - }); - } else { - // No file, just add the identifier - this.clientIdentities.push(newIdentity); - // Array mutation triggers @for re-render + let pending = files.length; + files.forEach((entry) => { + const formData = new FormData(); + formData.append('name', `${section.proof?.name || 'Document'} — ${entry.slot}`); + formData.append('file', entry.file); + this.clientService.uploadClientIdentifierDocument(newIdentifierId, formData).subscribe({ + next: (docRes: any) => { + const newDoc = { + id: docRes.resourceId, + parentEntityType: 'client_identifiers', + parentEntityId: newIdentifierId, + name: `${section.proof?.name || 'Document'} — ${entry.slot}`, + fileName: entry.file.name + }; + newIdentity.documents.push(newDoc); + this.setThumbnail(newDoc); + if (--pending === 0) { + this.clientIdentities.push(newIdentity); + } + }, + error: (err: any) => { + console.error('Failed to upload document', err); + if (--pending === 0) { + this.clientIdentities.push(newIdentity); + } } - }, - error: (err: any) => { - console.error('Failed to create identifier', err); - } + }); }); + }, + error: (err: any) => { + console.error('Failed to create identifier', err); } }); } @@ -245,14 +276,16 @@ export class IdentitiesTabComponent implements OnDestroy { * @param {number} index Index */ deleteIdentifier(clientId: string, identifierId: string, index: number) { + const identity = this.clientIdentities?.[index]; + const typeName = identity?.documentType?.name || this.translateService.instant('labels.heading.identifier id'); + const key = identity?.documentKey ? ` (${identity.documentKey})` : ''; const deleteIdentifierDialogRef = this.dialog.open(DeleteDialogComponent, { - data: { deleteContext: `${this.translateService.instant('labels.heading.identifier id')} : ${identifierId}` } + data: { deleteContext: `${typeName}${key}` } }); deleteIdentifierDialogRef.afterClosed().subscribe((response: any) => { - if (response.delete) { - this.clientService.deleteClientIdentifier(clientId, identifierId).subscribe((res) => { + if (response?.delete) { + this.clientService.deleteClientIdentifier(clientId, identifierId).subscribe(() => { this.clientIdentities.splice(index, 1); - // Array mutation triggers @for re-render }); } }); diff --git a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.html b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.html index 8433ca302..ca112928d 100644 --- a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.html +++ b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.html @@ -27,10 +27,17 @@ }
- +
+ @if (!isEditMode && kycData) { + + } + +
@@ -58,12 +65,22 @@ radio_button_unchecked Pending } - {{ kycData.panVerifiedOn ? formatDate(kycData.panVerifiedOn) : '—' }} + {{ + kycData.panVerifiedOn ? formatDate(kycData.panVerifiedOn) : '—' + }} -
@@ -81,11 +98,18 @@ radio_button_unchecked Pending } - {{ kycData.aadhaarVerifiedOn ? formatDate(kycData.aadhaarVerifiedOn) : '—' }} + {{ + kycData.aadhaarVerifiedOn ? formatDate(kycData.aadhaarVerifiedOn) : '—' + }} @if (!showAadhaarReVerify && !aadhaarOtpSent) { - } @@ -106,12 +130,22 @@ radio_button_unchecked Pending } - {{ kycData.gstinVerifiedOn ? formatDate(kycData.gstinVerifiedOn) : '—' }} + {{ + kycData.gstinVerifiedOn ? formatDate(kycData.gstinVerifiedOn) : '—' + }} -
@@ -131,7 +165,9 @@ radio_button_unchecked Pending } - {{ kycData.enterprisePanVerifiedOn ? formatDate(kycData.enterprisePanVerifiedOn) : '—' }} + {{ + kycData.enterprisePanVerifiedOn ? formatDate(kycData.enterprisePanVerifiedOn) : '—' + }}
} @@ -195,12 +231,27 @@
Enter 12-digit Aadhaar - + -
} @@ -209,32 +260,69 @@ sms OTP sent Enter OTP - + - + -
} @if (verificationError.aadhaar) { -
error_outline {{ verificationError.aadhaar }}
+
+ error_outline {{ verificationError.aadhaar }} +
} @if (verificationError.pan) { -
error_outline {{ verificationError.pan }}
+
+ error_outline {{ verificationError.pan }} +
}
KYC Type - {{ kycData.kycType === 1 ? 'Individual' : kycData.kycType === 2 ? 'Non-Individual' : '—' }} + {{ + kycData.kycType === 1 ? 'Individual' : kycData.kycType === 2 ? 'Non-Individual' : '—' + }}
Risk Category - {{ kycData.riskCategory === 1 ? 'Low' : kycData.riskCategory === 2 ? 'Medium' : kycData.riskCategory === 3 ? 'High' : '—' }} + {{ + kycData.riskCategory === 1 + ? 'Low' + : kycData.riskCategory === 2 + ? 'Medium' + : kycData.riskCategory === 3 + ? 'High' + : '—' + }}
Initiated On @@ -255,6 +343,105 @@
}
+ + +
+
+
+ +
+
+

Re-KYC Completion

+

Print self-declaration, get it signed, upload & complete re-KYC.

+
+
+ + +
+
+
+ 1 +
+
+
+
Download Self Declaration Form
+
System-generated PDF with PAN, Aadhaar & KYC details
+ +
+
+ + +
+
+ 2 +
+
+
+
Upload Signed Declaration
+
Get the form signed by the client and upload the scanned copy
+ +
+
+ + +
+
+ 3 +
+
+
Complete Re-KYC
+
Updates Last Verified date and sets new Expiry Date as per RBI norms
+ +
+
+
+ + @if (reKycSuccess) { +
+ +
+ Re-KYC completed successfully +

Last Verified and Expiry Date have been updated.

+
+
+ } + @if (reKycError) { +
+ error_outline +
+ Re-KYC failed +

{{ reKycError }}

+
+
+ } +
} diff --git a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.scss b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.scss index bd1d4db06..6a54b516d 100644 --- a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.scss +++ b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.scss @@ -164,6 +164,24 @@ color: #757575; border: 1px solid #e0e0e0; } + + &.linked { + background: #ecfdf5; + color: #059669; + border: 1px solid #a7f3d0; + } + + &.not-linked { + background: #fef2f2; + color: #dc2626; + border: 1px solid #fecaca; + } + + &.exempt { + background: #eff6ff; + color: #2563eb; + border: 1px solid #bfdbfe; + } } .status-chip-icon { @@ -375,26 +393,6 @@ margin-bottom: 16px; } -.status-chip { - &.linked { - background: #ecfdf5; - color: #059669; - border: 1px solid #a7f3d0; - } - - &.not-linked { - background: #fef2f2; - color: #dc2626; - border: 1px solid #fecaca; - } - - &.exempt { - background: #eff6ff; - color: #2563eb; - border: 1px solid #bfdbfe; - } -} - .pan-aadhaar-detail { margin: 12px 0; display: flex; @@ -468,10 +466,17 @@ padding: 0; transition: background 0.1s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } - &--link { background: #fafbfc; } + &:hover { + background: #f8fafc; + } + + &--link { + background: #fafbfc; + } } .kv-table__col { @@ -486,10 +491,25 @@ gap: 8px; font-weight: 600; } - &--num { flex: 1; } - &--status { flex: 0 0 150px; } - &--date { flex: 0 0 150px; color: #64748b; font-size: 12px; } - &--action { flex: 0 0 110px; text-align: right; } + + &--num { + flex: 1; + } + + &--status { + flex: 0 0 150px; + } + + &--date { + flex: 0 0 150px; + color: #64748b; + font-size: 12px; + } + + &--action { + flex: 0 0 110px; + text-align: right; + } } .kv-doc-icon { @@ -520,10 +540,25 @@ height: 14px !important; } - &--ok { background: #ecfdf5; color: #059669; } - &--pending { background: #f8fafc; color: #94a3b8; } - &--fail { background: #fef2f2; color: #dc2626; } - &--info { background: #eff6ff; color: #2563eb; } + &--ok { + background: #ecfdf5; + color: #059669; + } + + &--pending { + background: #f8fafc; + color: #94a3b8; + } + + &--fail { + background: #fef2f2; + color: #dc2626; + } + + &--info { + background: #eff6ff; + color: #2563eb; + } } .kv-action-btn { @@ -558,14 +593,21 @@ font-weight: 600; white-space: nowrap; - mat-icon { font-size: 16px !important; width: 16px !important; height: 16px !important; } + mat-icon { + font-size: 16px !important; + width: 16px !important; + height: 16px !important; + } } .kv-otp-input { width: 200px; flex-shrink: 0; - ::ng-deep .mat-mdc-form-field-infix { padding: 6px 0 !important; min-height: 36px !important; } + ::ng-deep .mat-mdc-form-field-infix { + padding: 6px 0 !important; + min-height: 36px !important; + } } .kv-otp-btn { @@ -594,8 +636,14 @@ border-right: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9; - &:nth-child(5n) { border-right: none; } - &--full { grid-column: 1 / -1; border-right: none; } + &:nth-child(5n) { + border-right: none; + } + + &--full { + grid-column: 1 / -1; + border-right: none; + } } .kv-meta-label { @@ -612,20 +660,332 @@ color: #1e293b; } -@media (max-width: 960px) { +@media (width <= 960px) { .kv-table__col { - &--doc { flex: 0 0 120px; } - &--date { display: none; } - &--action { flex: 0 0 90px; } + &--doc { + flex: 0 0 120px; + } + + &--date { + display: none; + } + + &--action { + flex: 0 0 90px; + } + } + + .kv-meta-grid { + grid-template-columns: repeat(3, 1fr); + } + + .kv-meta-item:nth-child(5n) { + border-right: 1px solid #f1f5f9; + } + + .kv-meta-item:nth-child(3n) { + border-right: none; } - .kv-meta-grid { grid-template-columns: repeat(3, 1fr); } - .kv-meta-item:nth-child(5n) { border-right: 1px solid #f1f5f9; } - .kv-meta-item:nth-child(3n) { border-right: none; } } -@media (max-width: 640px) { - .kv-table__row { flex-wrap: wrap; } - .kv-table__col { &--status, &--action { flex: 0 0 50%; } } - .kv-meta-grid { grid-template-columns: 1fr 1fr; } - .kv-otp-bar { flex-wrap: wrap; } +@media (width <= 640px) { + .kv-table__row { + flex-wrap: wrap; + } + + .kv-table__col { + &--status, + &--action { + flex: 0 0 50%; + } + } + + .kv-meta-grid { + grid-template-columns: 1fr 1fr; + } + + .kv-otp-bar { + flex-wrap: wrap; + } +} + +/* ─── Header Actions ─── */ +.kyc-header-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.kyc-print-btn { + display: flex; + align-items: center; + gap: 6px; + color: #1074b9 !important; + border-color: #93c5fd !important; +} + +.m-r-6 { + margin-right: 6px; +} + +/* ─── Re-KYC Section ─── */ +.rekyc-section { + margin-top: 20px; + background: linear-gradient(135deg, #fafbff 0%, #f0f4ff 100%); + border: 1px solid #dbe4f0; + border-radius: 14px; + padding: 24px; +} + +.rekyc-header { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 22px; +} + +.rekyc-icon-wrap { + width: 40px; + height: 40px; + border-radius: 10px; + background: linear-gradient(135deg, #1074b9, #0c5da0); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.rekyc-icon { + font-size: 17px; + color: #fff; +} + +.rekyc-header-text { + flex: 1; +} + +.rekyc-title { + margin: 0; + font-size: 16px; + font-weight: 700; + color: #0f172a; +} + +.rekyc-desc { + margin: 3px 0 0; + font-size: 12px; + color: #64748b; + line-height: 1.4; +} + +/* ─── Timeline Layout ─── */ +.rekyc-timeline { + display: flex; + flex-direction: column; + gap: 0; + padding-left: 4px; +} + +.rekyc-tl-item { + display: flex; + gap: 16px; +} + +.rekyc-tl-dot { + display: flex; + flex-direction: column; + align-items: center; + flex-shrink: 0; + width: 32px; +} + +.rekyc-tl-num { + width: 28px; + height: 28px; + border-radius: 50%; + background: #1074b9; + color: #fff; + font-size: 12px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + z-index: 1; +} + +.rekyc-tl-line { + width: 2px; + flex: 1; + background: #c7d8ed; + margin: 4px 0; +} + +.rekyc-tl-item--last .rekyc-tl-dot { + .rekyc-tl-line { + display: none; + } +} + +.rekyc-tl-content { + flex: 1; + padding-bottom: 24px; +} + +.rekyc-tl-item--last .rekyc-tl-content { + padding-bottom: 0; +} + +.rekyc-tl-label { + font-size: 13px; + font-weight: 700; + color: #1e293b; + margin-bottom: 2px; +} + +.rekyc-tl-sub { + font-size: 11px; + color: #94a3b8; + margin-bottom: 10px; + line-height: 1.4; +} + +/* ─── Buttons ─── */ +.rekyc-btn-print { + display: inline-flex !important; + align-items: center; + gap: 7px; + font-size: 12px !important; + font-weight: 600 !important; + color: #1074b9 !important; + background: #fff !important; + border: 1px solid #bfdbfe !important; + border-radius: 8px !important; + padding: 6px 16px !important; + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: #eff6ff !important; + border-color: #93c5fd !important; + } +} + +.rekyc-btn-complete { + display: inline-flex !important; + align-items: center; + gap: 7px; + font-size: 12px !important; + font-weight: 600 !important; + border-radius: 8px !important; + padding: 7px 20px !important; +} + +/* ─── Upload Zone ─── */ +.rekyc-upload-zone { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + padding: 16px 24px; + max-width: 280px; + background: #fff; + border: 2px dashed #cbd5e1; + border-radius: 10px; + cursor: pointer; + transition: all 0.2s; + text-align: center; + + &:hover { + background: #f8fafc; + border-color: #93c5fd; + } + + &.has-file { + background: #f0fdf4; + border-color: #86efac; + border-style: solid; + } +} + +.upload-zone-icon { + font-size: 28px !important; + width: 28px !important; + height: 28px !important; + color: #94a3b8; +} + +.upload-zone-text { + font-size: 12px; + font-weight: 600; + color: #475569; +} + +.upload-zone-hint { + font-size: 10px; + color: #94a3b8; +} + +.upload-zone-check { + font-size: 24px; + color: #22c55e; +} + +.upload-zone-filename { + font-size: 12px; + font-weight: 600; + color: #059669; + max-width: 230px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* ─── Result Banners ─── */ +.rekyc-result { + display: flex; + align-items: flex-start; + gap: 10px; + margin-top: 18px; + padding: 12px 16px; + border-radius: 10px; + font-size: 13px; + line-height: 1.4; + + p { + margin: 2px 0 0; + font-size: 12px; + font-weight: 400; + } + + &--success { + background: #ecfdf5; + border: 1px solid #a7f3d0; + color: #065f46; + + .rekyc-result-icon { + color: #059669; + font-size: 20px; + } + } + + &--error { + background: #fef2f2; + border: 1px solid #fecaca; + color: #991b1b; + + .rekyc-result-icon { + color: #dc2626; + font-size: 20px !important; + width: 20px !important; + height: 20px !important; + } + } +} + +@media (width <= 640px) { + .rekyc-upload-zone { + max-width: 100%; + } } diff --git a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.ts b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.ts index a744fabcd..e78e8cd26 100644 --- a/src/app/clients/clients-view/kyc-tab/kyc-tab.component.ts +++ b/src/app/clients/clients-view/kyc-tab/kyc-tab.component.ts @@ -15,6 +15,7 @@ import { catchError, of, Subject, takeUntil } from 'rxjs'; /** Custom Services */ import { ClientsService } from '../../clients.service'; import { Dates } from 'app/core/utils/dates'; +import { jsPDF } from 'jspdf'; import { MatIcon } from '@angular/material/icon'; import { MatTooltip } from '@angular/material/tooltip'; import { MatProgressSpinner } from '@angular/material/progress-spinner'; @@ -51,6 +52,7 @@ export class KycTabComponent implements OnInit, OnDestroy { clientId: string; kycId: string | null = null; kycData: any = null; + clientData: any = null; clientDateOfBirth: string | null = null; isNonIndividual = false; loading = true; @@ -58,6 +60,13 @@ export class KycTabComponent implements OnInit, OnDestroy { isEditMode = false; errorMessage: string | null = null; + /** Re-KYC flow state */ + reKycUploading = false; + reKycCompleting = false; + reKycSuccess = false; + reKycError: string | null = null; + selectedDeclarationFile: File | null = null; + verifying: { pan: boolean; aadhaar: boolean; gstin: boolean } = { pan: false, aadhaar: false, @@ -154,6 +163,7 @@ export class KycTabComponent implements OnInit, OnDestroy { /** Load client's date of birth and legal form from parent route resolved data */ loadClientDateOfBirth(): void { this.route.parent.data.subscribe((data: any) => { + this.clientData = data?.clientViewData || null; if (data?.clientViewData?.dateOfBirth) { const dob = data.clientViewData.dateOfBirth; if (Array.isArray(dob) && dob.length >= 3) { @@ -437,13 +447,264 @@ export class KycTabComponent implements OnInit, OnDestroy { getPanAadhaarStatusLabel(status: string): string { switch (status) { - case 'Y': return 'Linked (Seeded)'; - case 'N': return 'Not Linked'; - case 'E': return 'Exempt'; - default: return 'Pending Verification'; + case 'Y': + return 'Linked (Seeded)'; + case 'N': + return 'Not Linked'; + case 'E': + return 'Exempt'; + default: + return 'Pending Verification'; } } + /** Generate Self Declaration PDF with KYC details */ + printSelfDeclaration(): void { + const client = this.clientData || {}; + const kyc = this.kycData || {}; + const clientName = client.displayName || client.fullname || ''; + const cifNumber = client.accountNo || client.cifNumber || this.clientId; + const dob = this.clientDateOfBirth ? this.formatDate(client.dateOfBirth) : '—'; + const officeName = client.officeName || ''; + const today = new Date().toLocaleDateString('en-IN', { day: '2-digit', month: 'long', year: 'numeric' }); + + const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); + const pageWidth = doc.internal.pageSize.getWidth(); + const margin = 20; + const contentWidth = pageWidth - margin * 2; + let y = 20; + + // Helper + const addLine = (label: string, value: string, yPos: number): number => { + doc.setFont('helvetica', 'bold'); + doc.setFontSize(10); + doc.text(label, margin, yPos); + doc.setFont('helvetica', 'normal'); + doc.text(value || '—', margin + 55, yPos); + return yPos + 7; + }; + + const drawHr = (yPos: number): number => { + doc.setDrawColor(200, 200, 200); + doc.setLineWidth(0.3); + doc.line(margin, yPos, pageWidth - margin, yPos); + return yPos + 5; + }; + + // ── Header ── + doc.setFillColor(15, 23, 42); + doc.rect(0, 0, pageWidth, 30, 'F'); + doc.setTextColor(255, 255, 255); + doc.setFont('helvetica', 'bold'); + doc.setFontSize(16); + doc.text('Self Declaration Form', pageWidth / 2, 13, { align: 'center' }); + doc.setFontSize(9); + doc.setFont('helvetica', 'normal'); + doc.text('KYC / Re-KYC Compliance — As per RBI Master Direction on KYC', pageWidth / 2, 21, { align: 'center' }); + doc.setTextColor(0, 0, 0); + + y = 40; + + // ── Branch / Date ── + doc.setFontSize(10); + doc.setFont('helvetica', 'normal'); + doc.text(`Branch: ${officeName}`, margin, y); + doc.text(`Date: ${today}`, pageWidth - margin, y, { align: 'right' }); + y += 8; + y = drawHr(y); + + // ── Section: Customer Details ── + doc.setFont('helvetica', 'bold'); + doc.setFontSize(12); + doc.setTextColor(16, 116, 185); + doc.text('Customer Details', margin, y); + doc.setTextColor(0, 0, 0); + y += 8; + + y = addLine('Customer Name:', clientName, y); + y = addLine('CIF Number:', cifNumber, y); + y = addLine('Date of Birth:', dob, y); + if (client.mobileNo) y = addLine('Mobile:', client.mobileNo, y); + if (client.emailAddress) y = addLine('Email:', client.emailAddress, y); + y += 3; + y = drawHr(y); + + // ── Section: KYC Documents ── + doc.setFont('helvetica', 'bold'); + doc.setFontSize(12); + doc.setTextColor(16, 116, 185); + doc.text('KYC Document Details', margin, y); + doc.setTextColor(0, 0, 0); + y += 8; + + // PAN + y = addLine('PAN Number:', kyc.panNumber || '—', y); + y = addLine('PAN Status:', kyc.panVerified ? 'Verified' : 'Pending', y); + if (kyc.panVerifiedOn) y = addLine('PAN Verified On:', this.formatDate(kyc.panVerifiedOn), y); + + // Aadhaar + y = addLine('Aadhaar Number:', kyc.aadhaarNumberMasked || '—', y); + y = addLine('Aadhaar Status:', kyc.aadhaarVerified ? 'Verified' : 'Pending', y); + if (kyc.aadhaarVerifiedOn) y = addLine('Aadhaar Verified On:', this.formatDate(kyc.aadhaarVerifiedOn), y); + + // PAN-Aadhaar Link + const seedingStatus = + kyc.panAadhaarSeedingStatus === 'Y' + ? 'Linked' + : kyc.panAadhaarSeedingStatus === 'N' + ? 'Not Linked' + : kyc.panAadhaarSeedingStatus === 'E' + ? 'Exempt' + : 'Pending'; + y = addLine('PAN-Aadhaar Link:', seedingStatus, y); + + // GSTIN (Non-Individual) + if (this.isNonIndividual && kyc.gstinNumber) { + y = addLine('GSTIN:', kyc.gstinNumber, y); + y = addLine('GSTIN Status:', kyc.gstinVerified ? 'Verified' : 'Pending', y); + } + + // Enterprise PAN / CIN + if (this.isNonIndividual && kyc.enterprisePan) { + y = addLine('Enterprise PAN:', kyc.enterprisePan, y); + } + if (this.isNonIndividual && kyc.cinNumber) { + y = addLine('CIN:', kyc.cinNumber, y); + } + + y += 3; + y = drawHr(y); + + // ── Section: KYC Summary ── + doc.setFont('helvetica', 'bold'); + doc.setFontSize(12); + doc.setTextColor(16, 116, 185); + doc.text('KYC Summary', margin, y); + doc.setTextColor(0, 0, 0); + y += 8; + + y = addLine('KYC Type:', kyc.kycType === 1 ? 'Individual' : kyc.kycType === 2 ? 'Non-Individual' : '—', y); + y = addLine( + 'Risk Category:', + kyc.riskCategory === 1 ? 'Low' : kyc.riskCategory === 2 ? 'Medium' : kyc.riskCategory === 3 ? 'High' : '—', + y + ); + y = addLine('Initiated On:', this.formatDate(kyc.initiatedOn), y); + y = addLine('Last Verified:', this.formatDate(kyc.verifiedOn), y); + y = addLine('Expiry Date:', this.formatDate(kyc.expiryDate), y); + + y += 3; + y = drawHr(y); + + // ── Declaration Text ── + doc.setFont('helvetica', 'bold'); + doc.setFontSize(12); + doc.setTextColor(16, 116, 185); + doc.text('Declaration', margin, y); + doc.setTextColor(0, 0, 0); + y += 8; + + doc.setFont('helvetica', 'normal'); + doc.setFontSize(9.5); + const declarationText = `I, ${clientName || '_______________'}, hereby declare that the information provided above is true, correct and complete to the best of my knowledge and belief. I understand that furnishing false information is punishable under applicable laws. I authorize the Bank to verify the above details through appropriate channels as per RBI Master Direction on KYC (DBR.AML.BC.No.81/14.01.001/2015-16) and its subsequent amendments. + +I further confirm that: +1. I am not a Politically Exposed Person (PEP) / relative of a PEP (unless declared otherwise). +2. The source of funds in my account is from legitimate sources. +3. I shall promptly notify the Bank of any change in the above information. +4. I have read and understood the Bank's terms and conditions regarding KYC compliance.`; + + const lines = doc.splitTextToSize(declarationText, contentWidth); + doc.text(lines, margin, y); + y += lines.length * 4.5 + 10; + + // ── Signature section ── + y = drawHr(y); + y += 5; + + // Left: Client signature + doc.setFont('helvetica', 'normal'); + doc.setFontSize(9); + doc.text('________________________', margin, y + 15); + doc.text('Signature of Customer', margin, y + 21); + doc.text(`Name: ${clientName}`, margin, y + 27); + doc.text(`Date: _______________`, margin, y + 33); + doc.text(`Place: _______________`, margin, y + 39); + + // Right: Bank official + doc.text('________________________', pageWidth - margin - 50, y + 15); + doc.text('Authorized Bank Official', pageWidth - margin - 50, y + 21); + doc.text('Name: _______________', pageWidth - margin - 50, y + 27); + doc.text('Designation: _______________', pageWidth - margin - 50, y + 33); + doc.text('Stamp & Seal', pageWidth - margin - 50, y + 39); + + // ── Footer ── + const footerY = doc.internal.pageSize.getHeight() - 10; + doc.setFontSize(7); + doc.setTextColor(150, 150, 150); + doc.text('This is a system-generated document. Generated from CBS Nexora — KYC Module.', pageWidth / 2, footerY, { + align: 'center' + }); + + // Save + doc.save(`Self_Declaration_${cifNumber}_${clientName.replace(/\s/g, '_')}.pdf`); + } + + /** Handle file selection for signed declaration upload */ + onDeclarationFileSelected(event: Event): void { + const input = event.target as HTMLInputElement; + if (input.files?.length) { + this.selectedDeclarationFile = input.files[0]; + this.reKycError = null; + } + } + + /** Upload signed declaration and complete re-KYC */ + completeReKyc(): void { + if (!this.selectedDeclarationFile || !this.clientId || !this.kycId) return; + + this.reKycCompleting = true; + this.reKycError = null; + this.reKycSuccess = false; + + // Step 1: Upload the signed declaration as a client document + const formData = new FormData(); + formData.append('file', this.selectedDeclarationFile); + formData.append('name', 'KYC Self Declaration'); + formData.append( + 'description', + `Signed self-declaration form uploaded on ${new Date().toLocaleDateString('en-IN')}` + ); + + this.clientsService.uploadClientDocument(this.clientId, formData).subscribe({ + next: () => { + // Step 2: Call re-KYC completion API to update verified/expiry dates + this.clientsService.completeReKyc(this.clientId, this.kycId!).subscribe({ + next: () => { + this.reKycCompleting = false; + this.reKycSuccess = true; + this.selectedDeclarationFile = null; + this.loadKycData(); // Refresh to show updated dates + }, + error: (err: any) => { + this.reKycCompleting = false; + this.reKycError = + err?.error?.defaultUserMessage || + err?.error?.developerMessage || + 'Failed to complete re-KYC. Please try again.'; + } + }); + }, + error: (err: any) => { + this.reKycCompleting = false; + this.reKycError = + err?.error?.defaultUserMessage || + err?.error?.developerMessage || + 'Failed to upload declaration. Please try again.'; + } + }); + } + ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); diff --git a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.html b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.html index 375d9b7bd..c5f4498d7 100644 --- a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.html +++ b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.html @@ -6,7 +6,6 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
@@ -39,9 +38,12 @@
@for (group of accountGroups; track group.type) { @if (group.accounts.length > 0) { - } @@ -83,10 +95,12 @@
@@ -97,8 +111,12 @@
@if (nominee.profileImageUrl) { - + } @else { person } @@ -107,14 +125,22 @@ {{ nominee.nomineeDisplayName || nominee.firstName || '—' }} {{ getRelationship(nominee) }} - @if (nominee.nomineeDateOfBirth || nominee.dateOfBirth) { · DOB: {{ (nominee.nomineeDateOfBirth || nominee.dateOfBirth) | dateFormat }} } - @if (nominee.nomineeMobileNo || nominee.mobileNo) { · {{ nominee.nomineeMobileNo || nominee.mobileNo }} } + @if (nominee.nomineeDateOfBirth || nominee.dateOfBirth) { + · DOB: {{ nominee.nomineeDateOfBirth || nominee.dateOfBirth | dateFormat }} + } + @if (nominee.nomineeMobileNo || nominee.mobileNo) { + · {{ nominee.nomineeMobileNo || nominee.mobileNo }} + }
@if (nominee.signatureUrl) {
- Signature + Signature
} @if (nominee.nomineeIdentifier) { @@ -124,16 +150,30 @@
- - - + + +
@if (nominee.nomineeIsMinor && (nominee.guardianDisplayName || nominee.guardianName)) {
shield Guardian: {{ nominee.guardianDisplayName || nominee.guardianName }} - @if (nominee.guardianRelationshipName) { ({{ nominee.guardianRelationshipName }}) } + @if (nominee.guardianRelationshipName) { + ({{ nominee.guardianRelationshipName }}) + }
} @@ -143,24 +183,39 @@ Type - @for (opt of nominationTypeOptions; track opt.value) { {{ opt.label }} } + @for (opt of nominationTypeOptions; track opt.value) { + {{ opt.label }} + } Relationship - @for (rel of relationshipOptions; track rel) { {{ rel.name }} } + @for (rel of relationshipOptions; track rel) { + {{ rel.name }} + } Share % - @if (editShare && editShare > maxShareForEdit(i)) { Max {{ maxShareForEdit(i) }}% } + @if (editShare && editShare > maxShareForEdit(i)) { + Max {{ maxShareForEdit(i) }}% + } - - + (click)="saveEdit(nominee, i)" + > + check + +
} @@ -185,17 +240,39 @@ Search by Name, Client ID or Mobile search - + @if (searchResults.length > 0 || searching) {
- @if (searching) {
Searching...
} + @if (searching) { +
+ Searching... +
+ } @for (client of searchResults; track client.id) {
person
{{ client.displayName }} - ID: {{ client.id }} · {{ client.officeName }}@if (client.dateOfBirth) { · DOB: {{ client.dateOfBirth | dateFormat }} }@if (client.fatherSpouseName) { · {{ client.fatherSpouseName }} }@if (client.mobileNo) { · {{ client.mobileNo }} } + ID: {{ client.id }} · {{ client.officeName }} + @if (client.dateOfBirth) { + · DOB: {{ client.dateOfBirth | dateFormat }} + } + @if (client.fatherSpouseName) { + · {{ client.fatherSpouseName }} + } + @if (client.mobileNo) { + · {{ client.mobileNo }} + } +
} @@ -212,32 +289,68 @@
@if (linkedProfileImage) { - - } @else { person } + + } @else { + person + }
{{ selectedClient.displayName }} - ID: {{ selectedClient.id }}@if (selectedClient.mobileNo) { · {{ selectedClient.mobileNo }} } + ID: {{ selectedClient.id }} + @if (selectedClient.mobileNo) { + · {{ selectedClient.mobileNo }} + } +
@if (linkedSignatureImage) {
- Signature + Signature
}
@if (fetchingDetails) { -
Fetching details...
+
+ Fetching details... +
} @if (!fetchingDetails) { @if (selectedClient.dateOfBirth || selectedClient.panNumber || selectedClient.aadhaarMasked) {
- @if (selectedClient.dateOfBirth) { DOB {{ selectedClient.dateOfBirth | dateFormat }} } - @if (selectedClient.panNumber) { PAN {{ selectedClient.panNumber }} @if (linkedKycStatus?.panVerified) { check_circle } } - @if (selectedClient.aadhaarMasked) { Aadhaar {{ selectedClient.aadhaarMasked }} @if (linkedKycStatus?.aadhaarVerified) { check_circle } } + @if (selectedClient.dateOfBirth) { + DOB {{ selectedClient.dateOfBirth | dateFormat }} + } + @if (selectedClient.panNumber) { + PAN {{ selectedClient.panNumber }} + @if (linkedKycStatus?.panVerified) { + check_circle + } + + } + @if (selectedClient.aadhaarMasked) { + Aadhaar {{ selectedClient.aadhaarMasked }} + @if (linkedKycStatus?.aadhaarVerified) { + check_circle + } + + }
} @@ -246,19 +359,25 @@ Nomination Type - @for (opt of nominationTypeOptions; track opt.value) { {{ opt.label }} } + @for (opt of nominationTypeOptions; track opt.value) { + {{ opt.label }} + } Relationship - @for (rel of relationshipOptions; track rel) { {{ rel.name }} } + @for (rel of relationshipOptions; track rel) { + {{ rel.name }} + } Share % - @if (nomineeShare && nomineeShare > maxAllowedShare) { Max {{ maxAllowedShare }}% } + @if (nomineeShare && nomineeShare > maxAllowedShare) { + Max {{ maxAllowedShare }}% + }
Minor @@ -270,11 +389,21 @@ shield Guardian @if (!selectedGuardian) { - + @if (guardianSearchResults.length > 0 || guardianSearching) {
- @if (guardianSearching) {
} + @if (guardianSearching) { +
+ +
+ } @for (client of guardianSearchResults; track client.id) {
person @@ -284,7 +413,12 @@
} } @else { - {{ selectedGuardian.displayName }} + {{ selectedGuardian.displayName }} + }
} @@ -296,11 +430,24 @@
-
} diff --git a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.scss b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.scss index c1c46b387..c5980ff99 100644 --- a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.scss +++ b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.scss @@ -8,176 +8,708 @@ $primary: #1074b9; $primary-light: #e8f0fe; -.nom-container { padding: 16px 24px; } +.nom-container { + padding: 16px 24px; +} /* Header */ .nom-page-header { - background: #0f172a; padding: 14px 20px; border-radius: 10px; margin-bottom: 16px; - &__left { display: flex; align-items: center; gap: 10px; } - &__icon { color: #60a5fa; } - &__title { font-size: 15px; font-weight: 600; color: #fff; margin: 0; } - &__sub { font-size: 10px; color: rgba(255,255,255,0.45); margin: 1px 0 0; } + background: #0f172a; + padding: 14px 20px; + border-radius: 10px; + margin-bottom: 16px; + + &__left { + display: flex; + align-items: center; + gap: 10px; + } + + &__icon { + color: #60a5fa; + } + + &__title { + font-size: 15px; + font-weight: 600; + color: #fff; + margin: 0; + } + + &__sub { + font-size: 10px; + color: rgb(255 255 255 / 45%); + margin: 1px 0 0; + } } -.nom-loading { display: flex; align-items: center; gap: 8px; padding: 24px 0; justify-content: center; font-size: 13px; color: #64748b; - &--sm { padding: 12px 0; } +.nom-loading { + display: flex; + align-items: center; + gap: 8px; + padding: 24px 0; + justify-content: center; + font-size: 13px; + color: #64748b; + + &--sm { + padding: 12px 0; + } } .nom-no-accounts { - text-align: center; padding: 36px 20px; background: #f8fafc; border-radius: 8px; border: 1px dashed #e2e8f0; - &__icon { font-size: 40px; width: 40px; height: 40px; color: #94a3b8; margin-bottom: 8px; } - p { color: #64748b; font-size: 13px; margin: 2px 0; } - &__sub { font-size: 11px; color: #94a3b8; } + text-align: center; + padding: 36px 20px; + background: #f8fafc; + border-radius: 8px; + border: 1px dashed #e2e8f0; + + &__icon { + font-size: 40px; + width: 40px; + height: 40px; + color: #94a3b8; + margin-bottom: 8px; + } + + p { + color: #64748b; + font-size: 13px; + margin: 2px 0; + } + + &__sub { + font-size: 11px; + color: #94a3b8; + } } /* Toolbar: chips on top, account + add below */ .nom-toolbar { - display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; - &__right { display: flex; align-items: center; gap: 10px; } -} -.nom-type-chips { display: flex; gap: 6px; flex-wrap: wrap; } -.nom-type-chip { - display: inline-flex; align-items: center; gap: 5px; padding: 6px 12px; - border: 1.5px solid #e2e8f0; border-radius: 6px; background: #fff; - font-size: 12px; font-weight: 500; color: #64748b; cursor: pointer; transition: all 0.15s; - mat-icon { font-size: 16px; width: 16px; height: 16px; } - &__count { font-size: 9px; font-weight: 700; background: #f1f5f9; color: #64748b; padding: 0 5px; border-radius: 6px; } - &.active { border-color: $primary; background: $primary-light; color: $primary; - .nom-type-chip__count { background: $primary; color: #fff; } + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 12px; + + &__right { + display: flex; + align-items: center; + gap: 10px; } - &:hover:not(.active) { border-color: #94a3b8; } } -.nom-account-select { flex: 1; min-width: 280px; } -.nom-add-btn { border-radius: 6px !important; font-size: 12px !important; height: 36px !important; white-space: nowrap; } + +.nom-type-chips { + display: flex; + gap: 6px; + flex-wrap: wrap; +} + +.nom-type-chip { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 6px 12px; + border: 1.5px solid #e2e8f0; + border-radius: 6px; + background: #fff; + font-size: 12px; + font-weight: 500; + color: #64748b; + cursor: pointer; + transition: all 0.15s; + + mat-icon { + font-size: 16px; + width: 16px; + height: 16px; + } + + &__count { + font-size: 9px; + font-weight: 700; + background: #f1f5f9; + color: #64748b; + padding: 0 5px; + border-radius: 6px; + } + + &.active { + border-color: $primary; + background: $primary-light; + color: $primary; + + .nom-type-chip__count { + background: $primary; + color: #fff; + } + } + + &:hover:not(.active) { + border-color: #94a3b8; + } +} + +.nom-account-select { + flex: 1; + min-width: 280px; +} + +.nom-add-btn { + border-radius: 6px !important; + font-size: 12px !important; + height: 36px !important; + white-space: nowrap; +} /* Share bar */ .nom-share-bar { - display: flex; align-items: center; gap: 8px; padding: 6px 12px; - background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 8px; - &__label { font-size: 11px; font-weight: 600; color: #64748b; white-space: nowrap; } - &__track { flex: 1; height: 6px; background: #e2e8f0; border-radius: 3px; overflow: hidden; } + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 6px; + margin-bottom: 8px; + + &__label { + font-size: 11px; + font-weight: 600; + color: #64748b; + white-space: nowrap; + } + + &__track { + flex: 1; + height: 6px; + background: #e2e8f0; + border-radius: 3px; + overflow: hidden; + } + &__fill { - height: 100%; border-radius: 3px; transition: width 0.3s; + height: 100%; + border-radius: 3px; + transition: width 0.3s; background: linear-gradient(90deg, #3b82f6, #10b981); - &.over { background: #ef4444; } &.complete { background: linear-gradient(90deg, #10b981, #059669); } + + &.over { + background: #ef4444; + } + + &.complete { + background: linear-gradient(90deg, #10b981, #059669); + } + } + + &__value { + font-size: 13px; + font-weight: 700; + color: #10b981; + min-width: 35px; + text-align: right; + + &.over { + color: #ef4444; + } } - &__value { font-size: 13px; font-weight: 700; color: #10b981; min-width: 35px; text-align: right; &.over { color: #ef4444; } } } /* Nominee card — compact */ -.nom-card { border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 6px; overflow: hidden; } -.nom-card__main { display: flex; align-items: center; gap: 10px; padding: 8px 12px; } +.nom-card { + border: 1px solid #e2e8f0; + border-radius: 6px; + margin-bottom: 6px; + overflow: hidden; +} + +.nom-card__main { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; +} + .nom-card__avatar { - width: 32px; height: 32px; border-radius: 50%; background: #dbeafe; - display: flex; align-items: center; justify-content: center; color: #1565c0; flex-shrink: 0; - mat-icon { font-size: 18px; } -} -.nom-card__photo { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; } -.nom-card__sig { + width: 32px; + height: 32px; + border-radius: 50%; + background: #dbeafe; + display: flex; + align-items: center; + justify-content: center; + color: #1565c0; flex-shrink: 0; - img { height: 24px; max-width: 60px; object-fit: contain; border: 1px solid #e2e8f0; border-radius: 3px; background: #fff; padding: 1px 3px; } -} -.nom-card__info { flex: 1; min-width: 0; } -.nom-card__name { font-weight: 600; font-size: 13px; color: #1e293b; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.nom-card__meta { font-size: 11px; color: #64748b; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.nom-card__id { - font-size: 10px; font-weight: 600; color: #1d4ed8; background: #eff6ff; - padding: 2px 8px; border-radius: 4px; white-space: nowrap; - font-family: 'Roboto Mono', monospace; letter-spacing: 0.3px; flex-shrink: 0; -} -.nom-card__share { text-align: center; min-width: 40px; flex-shrink: 0; } -.nom-card__share-value { font-size: 15px; font-weight: 700; color: #1565c0; } -.nom-card__actions { display: flex; gap: 0; flex-shrink: 0; - button { width: 32px !important; height: 32px !important; line-height: 32px !important; - mat-icon { font-size: 18px; } + + mat-icon { + font-size: 18px; } } -.nom-card__delete { color: #94a3b8 !important; &:hover { color: #ef4444 !important; } } + +.nom-card__photo { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.nom-card__sig { + flex-shrink: 0; + + img { + height: 24px; + max-width: 60px; + object-fit: contain; + border: 1px solid #e2e8f0; + border-radius: 3px; + background: #fff; + padding: 1px 3px; + } +} + +.nom-card__info { + flex: 1; + min-width: 0; +} + +.nom-card__name { + font-weight: 600; + font-size: 13px; + color: #1e293b; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.nom-card__meta { + font-size: 11px; + color: #64748b; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.nom-card__id { + font-size: 10px; + font-weight: 600; + color: #1d4ed8; + background: #eff6ff; + padding: 2px 8px; + border-radius: 4px; + white-space: nowrap; + font-family: 'Roboto Mono', monospace; + letter-spacing: 0.3px; + flex-shrink: 0; +} + +.nom-card__share { + text-align: center; + min-width: 40px; + flex-shrink: 0; +} + +.nom-card__share-value { + font-size: 15px; + font-weight: 700; + color: #1565c0; +} + +.nom-card__actions { + display: flex; + gap: 0; + flex-shrink: 0; + + button { + width: 32px !important; + height: 32px !important; + line-height: 32px !important; + + mat-icon { + font-size: 18px; + } + } +} + +.nom-card__delete { + color: #94a3b8 !important; + + &:hover { + color: #ef4444 !important; + } +} + .nom-card__guardian { - display: flex; align-items: center; gap: 5px; padding: 5px 12px; background: #fff7ed; - font-size: 11px; color: #92400e; border-top: 1px solid #fde68a; - mat-icon { font-size: 14px; width: 14px; height: 14px; } + display: flex; + align-items: center; + gap: 5px; + padding: 5px 12px; + background: #fff7ed; + font-size: 11px; + color: #92400e; + border-top: 1px solid #fde68a; + + mat-icon { + font-size: 14px; + width: 14px; + height: 14px; + } } /* Inline edit */ -.nom-card__edit { padding: 8px 12px; border-top: 1px solid #e2e8f0; background: #f8fafc; } -.nom-edit-row { display: flex; align-items: center; gap: 8px; } -.nom-edit-field { flex: 1; &--sm { max-width: 100px; } } -.nom-edit-save, .nom-edit-cancel { min-width: 36px !important; height: 36px !important; padding: 0 !important; } +.nom-card__edit { + padding: 8px 12px; + border-top: 1px solid #e2e8f0; + background: #f8fafc; +} + +.nom-edit-row { + display: flex; + align-items: center; + gap: 8px; +} + +.nom-edit-field { + flex: 1; + + &--sm { + max-width: 100px; + } +} + +.nom-edit-save, +.nom-edit-cancel { + min-width: 36px !important; + height: 36px !important; + padding: 0 !important; +} /* Empty */ .nom-empty { - text-align: center; padding: 24px; background: #f8fafc; border-radius: 6px; border: 1px dashed #e2e8f0; - mat-icon { font-size: 28px; width: 28px; height: 28px; color: #94a3b8; margin-bottom: 4px; display: block; margin: 0 auto 4px; } - span { font-size: 12px; color: #94a3b8; } + text-align: center; + padding: 24px; + background: #f8fafc; + border-radius: 6px; + border: 1px dashed #e2e8f0; + + mat-icon { + font-size: 28px; + width: 28px; + height: 28px; + color: #94a3b8; + display: block; + margin: 0 auto 4px; + } + + span { + font-size: 12px; + color: #94a3b8; + } } /* ═══ Add Form ═══ */ .nom-add-form { - border: 1px solid #e2e8f0; border-radius: 8px; padding: 14px; margin-top: 8px; background: #fafbfc; - &__title { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: #1e293b; margin-bottom: 12px; } + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 14px; + margin-top: 8px; + background: #fafbfc; + + &__title { + display: flex; + align-items: center; + gap: 6px; + font-size: 13px; + font-weight: 600; + color: #1e293b; + margin-bottom: 12px; + } } /* Search */ -.nom-search-wrapper { position: relative; margin-bottom: 8px; } -.nom-search-field { width: 100%; } -.nom-search-prefix { color: #94a3b8; } +.nom-search-wrapper { + position: relative; + margin-bottom: 8px; +} + +.nom-search-field { + width: 100%; +} + +.nom-search-prefix { + color: #94a3b8; +} + .nom-search-results { - position: absolute; top: 56px; left: 0; right: 0; z-index: 100; - background: #fff; border: 1px solid #e2e8f0; border-radius: 6px; - box-shadow: 0 6px 20px rgba(0,0,0,0.1); max-height: 220px; overflow-y: auto; - &--inline { position: relative; top: 0; margin-top: 4px; } + position: absolute; + top: 56px; + left: 0; + right: 0; + z-index: 100; + background: #fff; + border: 1px solid #e2e8f0; + border-radius: 6px; + box-shadow: 0 6px 20px rgb(0 0 0 / 10%); + max-height: 220px; + overflow-y: auto; + + &--inline { + position: relative; + top: 0; + margin-top: 4px; + } } -.nom-search-loading { display: flex; align-items: center; gap: 6px; padding: 10px 12px; font-size: 12px; color: #64748b; } + +.nom-search-loading { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 12px; + font-size: 12px; + color: #64748b; +} + .nom-search-item { - display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } &:hover { background: #f0f7ff; } - &__icon { color: $primary; font-size: 18px; } - &__info { display: flex; flex-direction: column; flex: 1; } - &__name { font-size: 13px; font-weight: 600; color: #1e293b; } - &__meta { font-size: 10px; color: #64748b; } + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + cursor: pointer; + border-bottom: 1px solid #f1f5f9; + + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f0f7ff; + } + + &__icon { + color: $primary; + font-size: 18px; + } + + &__info { + display: flex; + flex-direction: column; + flex: 1; + } + + &__name { + font-size: 13px; + font-weight: 600; + color: #1e293b; + } + + &__meta { + font-size: 10px; + color: #64748b; + } +} + +.search-no-results-hint { + font-size: 11px; + color: #94a3b8; + text-align: center; + padding: 4px 0; } -.search-no-results-hint { font-size: 11px; color: #94a3b8; text-align: center; padding: 4px 0; } /* Linked card */ .nom-linked-card { - display: flex; align-items: center; gap: 8px; padding: 8px 12px; - background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 8px; - &__left { flex-shrink: 0; } - &__icon { color: $primary; } - &__photo { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; border: 2px solid #e2e8f0; } - &__info { flex: 1; display: flex; flex-direction: column; } - &__name { font-size: 13px; font-weight: 600; color: #1e293b; } - &__meta { font-size: 11px; color: #64748b; font-family: 'Roboto Mono', monospace; } + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 6px; + margin-bottom: 8px; + + &__left { + flex-shrink: 0; + } + + &__icon { + color: $primary; + } + + &__photo { + width: 36px; + height: 36px; + border-radius: 50%; + object-fit: cover; + border: 2px solid #e2e8f0; + } + + &__info { + flex: 1; + display: flex; + flex-direction: column; + } + + &__name { + font-size: 13px; + font-weight: 600; + color: #1e293b; + } + + &__meta { + font-size: 11px; + color: #64748b; + font-family: 'Roboto Mono', monospace; + } + &__sig { flex-shrink: 0; - img { height: 24px; max-width: 60px; object-fit: contain; border: 1px solid #e2e8f0; border-radius: 3px; background: #fff; padding: 1px 3px; } + + img { + height: 24px; + max-width: 60px; + object-fit: contain; + border: 1px solid #e2e8f0; + border-radius: 3px; + background: #fff; + padding: 1px 3px; + } } } -.nom-fetching-hint { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #64748b; margin: -4px 0 8px; } + +.nom-fetching-hint { + display: flex; + align-items: center; + gap: 6px; + font-size: 11px; + color: #64748b; + margin: -4px 0 8px; +} /* Read-only KYC row */ .nom-readonly-row { - display: flex; flex-wrap: wrap; gap: 16px; padding: 8px 12px; - background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 10px; + display: flex; + flex-wrap: wrap; + gap: 16px; + padding: 8px 12px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 6px; + margin-bottom: 10px; +} + +.nom-ro-item { + font-size: 12px; + color: #1e293b; + display: inline-flex; + align-items: center; + gap: 4px; +} + +.nom-ro-label { + font-size: 10px; + font-weight: 600; + color: #94a3b8; + text-transform: uppercase; + margin-right: 2px; +} + +.nom-ro-ok { + font-size: 14px !important; + width: 14px !important; + height: 14px !important; + color: #16a34a; } -.nom-ro-item { font-size: 12px; color: #1e293b; display: inline-flex; align-items: center; gap: 4px; } -.nom-ro-label { font-size: 10px; font-weight: 600; color: #94a3b8; text-transform: uppercase; margin-right: 2px; } -.nom-ro-ok { font-size: 14px !important; width: 14px !important; height: 14px !important; color: #16a34a; } /* Nominee fields */ -.nom-fields-row { display: flex; flex-wrap: wrap; gap: 0 12px; align-items: center; margin-bottom: 4px; } -.nom-field-item { flex: 1; min-width: 160px; &--sm { max-width: 100px; min-width: 80px; } &--toggle { padding-top: 8px; } } -.nom-remarks-field { width: 100%; } -.nom-form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; } +.nom-fields-row { + display: flex; + flex-wrap: wrap; + gap: 0 12px; + align-items: center; + margin-bottom: 4px; +} + +.nom-field-item { + flex: 1; + min-width: 160px; + + &--sm { + max-width: 100px; + min-width: 80px; + } + + &--toggle { + padding-top: 8px; + } +} + +.nom-remarks-field { + width: 100%; +} + +.nom-form-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 4px; +} /* Guardian */ -.nom-guardian-section { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; } -.nom-guardian-label { display: flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600; color: #92400e; mat-icon { font-size: 16px; } } -.nom-guardian-selected { display: flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 500; color: #1e293b; - button { width: 24px !important; height: 24px !important; line-height: 24px !important; mat-icon { font-size: 14px; } } +.nom-guardian-section { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 8px; +} + +.nom-guardian-label { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + font-weight: 600; + color: #92400e; + + mat-icon { + font-size: 16px; + } +} + +.nom-guardian-selected { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; + font-weight: 500; + color: #1e293b; + + button { + width: 24px !important; + height: 24px !important; + line-height: 24px !important; + + mat-icon { + font-size: 14px; + } + } } /* Zoom */ -.zoomable { cursor: zoom-in; transition: transform 0.15s; &:hover { transform: scale(1.08); box-shadow: 0 4px 12px rgba(0,0,0,0.15); } } -::ng-deep .image-zoom-panel { .mat-mdc-dialog-container { padding: 0 !important; border-radius: 8px !important; overflow: hidden; } } +.zoomable { + cursor: zoom-in; + transition: transform 0.15s; + + &:hover { + transform: scale(1.08); + box-shadow: 0 4px 12px rgb(0 0 0 / 15%); + } +} + +::ng-deep .image-zoom-panel { + .mat-mdc-dialog-container { + padding: 0 !important; + border-radius: 8px !important; + overflow: hidden; + } +} diff --git a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.ts b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.ts index 32b24cb34..eb6e4a236 100644 --- a/src/app/clients/clients-view/nominee-tab/nominee-tab.component.ts +++ b/src/app/clients/clients-view/nominee-tab/nominee-tab.component.ts @@ -109,11 +109,11 @@ export class NomineeTabComponent implements OnInit { editNomType: string = 'DA1'; get hasAccounts(): boolean { - return this.accountGroups.some(g => g.accounts.length > 0); + return this.accountGroups.some((g) => g.accounts.length > 0); } get selectedGroup(): AccountGroup | undefined { - return this.accountGroups.find(g => g.type === this.selectedType); + return this.accountGroups.find((g) => g.type === this.selectedType); } get selectedAccount(): any { @@ -154,13 +154,43 @@ export class NomineeTabComponent implements OnInit { next: (data: any) => { this.loading = false; this.accountGroups = [ - { type: 'savings', apiType: 'SB', label: 'Savings Account', icon: 'savings', accounts: this.filterActive(data.savingsAccounts) }, - { type: 'current', apiType: 'CB', label: 'Current Account', icon: 'account_balance', accounts: this.filterActive(data.currentAccounts) }, - { type: 'fd', apiType: 'FD', label: 'Fixed Deposit', icon: 'lock', accounts: this.filterActive(data.fixedDepositAccounts) }, - { type: 'rd', apiType: 'RD', label: 'Recurring Deposit', icon: 'autorenew', accounts: this.filterActive(data.recurringDepositAccounts) }, - { type: 'loan', apiType: 'LA', label: 'Loan Account', icon: 'credit_score', accounts: this.filterActive(data.loanAccounts) } + { + type: 'savings', + apiType: 'SB', + label: 'Savings Account', + icon: 'savings', + accounts: this.filterActive(data.savingsAccounts) + }, + { + type: 'current', + apiType: 'CB', + label: 'Current Account', + icon: 'account_balance', + accounts: this.filterActive(data.currentAccounts) + }, + { + type: 'fd', + apiType: 'FD', + label: 'Fixed Deposit', + icon: 'lock', + accounts: this.filterActive(data.fixedDepositAccounts) + }, + { + type: 'rd', + apiType: 'RD', + label: 'Recurring Deposit', + icon: 'autorenew', + accounts: this.filterActive(data.recurringDepositAccounts) + }, + { + type: 'loan', + apiType: 'LA', + label: 'Loan Account', + icon: 'credit_score', + accounts: this.filterActive(data.loanAccounts) + } ]; - const firstWithAccounts = this.accountGroups.find(g => g.accounts.length > 0); + const firstWithAccounts = this.accountGroups.find((g) => g.accounts.length > 0); if (firstWithAccounts) { this.selectedType = firstWithAccounts.type; // Auto-select if only 1 account in the group @@ -170,18 +200,16 @@ export class NomineeTabComponent implements OnInit { } } }, - error: () => { this.loading = false; } + error: () => { + this.loading = false; + } }); } private filterActive(accounts: any[]): any[] { if (!accounts) return []; // Include all non-closed accounts (active, disbursed, pending, etc.) - return accounts.filter((a: any) => - a.status?.active || - a.status?.value === 'Active' || - !a.status?.closed - ); + return accounts.filter((a: any) => a.status?.active || a.status?.value === 'Active' || !a.status?.closed); } onTypeChange(): void { @@ -198,7 +226,9 @@ export class NomineeTabComponent implements OnInit { onAccountChange(): void { this.nominees = []; this.resetAddForm(); - if (this.selectedAccountId) { this.loadNominees(); } + if (this.selectedAccountId) { + this.loadNominees(); + } } private loadNominees(): void { @@ -210,9 +240,12 @@ export class NomineeTabComponent implements OnInit { this.nomineesLoading = false; this.nominees = Array.isArray(data) ? data : []; // Enrich each nominee with profile image & signature - this.nominees.forEach(n => this.enrichNomineeImages(n)); + this.nominees.forEach((n) => this.enrichNomineeImages(n)); }, - error: () => { this.nomineesLoading = false; this.nominees = []; } + error: () => { + this.nomineesLoading = false; + this.nominees = []; + } }); } @@ -224,7 +257,9 @@ export class NomineeTabComponent implements OnInit { // Profile image this.clientsService.getClientProfileImage(cid).subscribe({ next: (img: any) => { - if (img) { nominee.profileImageUrl = this.sanitizer.bypassSecurityTrustResourceUrl(img); } + if (img) { + nominee.profileImageUrl = this.sanitizer.bypassSecurityTrustResourceUrl(img); + } }, error: () => {} }); @@ -232,8 +267,8 @@ export class NomineeTabComponent implements OnInit { // Signature this.clientsService.getClientDocuments(cid).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => - d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') ); if (sigDoc) { this.clientsService.getClientSignatureImage(cid, sigDoc.id.toString()).subscribe({ @@ -253,7 +288,10 @@ export class NomineeTabComponent implements OnInit { onSearch(text: string): void { this.searchText = text; clearTimeout(this.searchTimer); - if (!text || text.length < 2) { this.searchResults = []; return; } + if (!text || text.length < 2) { + this.searchResults = []; + return; + } this.searching = true; this.searchTimer = setTimeout(() => { this.clientsService.searchByText(text, 0, 8).subscribe({ @@ -262,13 +300,19 @@ export class NomineeTabComponent implements OnInit { this.searching = false; this.clientsService.enrichSearchResults(this.searchResults); }, - error: () => { this.searchResults = []; this.searching = false; } + error: () => { + this.searchResults = []; + this.searching = false; + } }); }, 400); } dismissSearch(): void { - setTimeout(() => { this.searchResults = []; this.searching = false; }, 200); + setTimeout(() => { + this.searchResults = []; + this.searching = false; + }, 200); } selectClient(client: any): void { @@ -291,11 +335,18 @@ export class NomineeTabComponent implements OnInit { this.linkedSignatureImage = null; this.linkedKycStatus = null; let pending = 4; - const done = () => { pending--; if (pending <= 0) { this.fetchingDetails = false; } }; + const done = () => { + pending--; + if (pending <= 0) { + this.fetchingDetails = false; + } + }; this.clientsService.getClientData(clientId.toString()).subscribe({ next: (data: any) => { - if (data?.dateOfBirth) { this.selectedClient = { ...this.selectedClient, dateOfBirth: data.dateOfBirth }; } + if (data?.dateOfBirth) { + this.selectedClient = { ...this.selectedClient, dateOfBirth: data.dateOfBirth }; + } done(); }, error: () => done() @@ -304,8 +355,15 @@ export class NomineeTabComponent implements OnInit { this.clientsService.getClientKyc(clientId.toString()).subscribe({ next: (kyc: any) => { if (kyc) { - this.selectedClient = { ...this.selectedClient, panNumber: kyc.panNumber, aadhaarMasked: kyc.aadhaarNumberMasked }; - this.linkedKycStatus = { panVerified: kyc.panVerified || false, aadhaarVerified: kyc.aadhaarVerified || false }; + this.selectedClient = { + ...this.selectedClient, + panNumber: kyc.panNumber, + aadhaarMasked: kyc.aadhaarNumberMasked + }; + this.linkedKycStatus = { + panVerified: kyc.panVerified || false, + aadhaarVerified: kyc.aadhaarVerified || false + }; } done(); }, @@ -313,19 +371,31 @@ export class NomineeTabComponent implements OnInit { }); this.clientsService.getClientProfileImage(clientId.toString()).subscribe({ - next: (img: any) => { if (img) { this.linkedProfileImage = this.sanitizer.bypassSecurityTrustResourceUrl(img); } done(); }, + next: (img: any) => { + if (img) { + this.linkedProfileImage = this.sanitizer.bypassSecurityTrustResourceUrl(img); + } + done(); + }, error: () => done() }); this.clientsService.getClientDocuments(clientId.toString()).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature')); + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + ); if (sigDoc) { this.clientsService.getClientSignatureImage(clientId.toString(), sigDoc.id.toString()).subscribe({ - next: (blob: Blob) => { this.linkedSignatureImage = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); done(); }, + next: (blob: Blob) => { + this.linkedSignatureImage = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); + done(); + }, error: () => done() }); - } else { done(); } + } else { + done(); + } }, error: () => done() }); @@ -336,12 +406,22 @@ export class NomineeTabComponent implements OnInit { onGuardianSearch(text: string): void { this.guardianSearchText = text; clearTimeout(this.guardianSearchTimer); - if (!text || text.length < 2) { this.guardianSearchResults = []; return; } + if (!text || text.length < 2) { + this.guardianSearchResults = []; + return; + } this.guardianSearching = true; this.guardianSearchTimer = setTimeout(() => { this.clientsService.searchByText(text, 0, 8).subscribe({ - next: (data: any) => { this.guardianSearchResults = data?.content || []; this.guardianSearching = false; this.clientsService.enrichSearchResults(this.guardianSearchResults); }, - error: () => { this.guardianSearchResults = []; this.guardianSearching = false; } + next: (data: any) => { + this.guardianSearchResults = data?.content || []; + this.guardianSearching = false; + this.clientsService.enrichSearchResults(this.guardianSearchResults); + }, + error: () => { + this.guardianSearchResults = []; + this.guardianSearching = false; + } }); }, 400); } @@ -385,7 +465,9 @@ export class NomineeTabComponent implements OnInit { this.loadNominees(); this.resetAddForm(); }, - error: () => { this.nomineeSaving = false; } + error: () => { + this.nomineeSaving = false; + } }); } @@ -395,26 +477,30 @@ export class NomineeTabComponent implements OnInit { // Resolve the linked client ID — could be nomineeClientId or nested const clientId = nominee.nomineeClientId || nominee.linkedClientId || nominee.clientId; const displayName = nominee.nomineeDisplayName || nominee.firstName || ''; - import('../../client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component').then(m => { - this.dialog.open(m.ClientFamilyMemberDialogComponent, { - data: { - context: 'View', - member: { - firstName: displayName, - relationshipId: nominee.relationshipId, - dateOfBirth: nominee.nomineeDateOfBirth || nominee.dateOfBirth, - linkedClientId: clientId, - linkedClientName: displayName, - isAlive: true, - isDependent: nominee.nomineeIsMinor, - panNumber: nominee.panNumber || '', - aadhaarNumber: nominee.aadhaarMasked || '' + import('../../client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component').then( + (m) => { + this.dialog.open(m.ClientFamilyMemberDialogComponent, { + data: { + context: 'View', + member: { + firstName: displayName, + relationshipId: nominee.relationshipId, + dateOfBirth: nominee.nomineeDateOfBirth || nominee.dateOfBirth, + linkedClientId: clientId, + linkedClientName: displayName, + isAlive: true, + isDependent: nominee.nomineeIsMinor, + panNumber: nominee.panNumber || '', + aadhaarNumber: nominee.aadhaarMasked || '' + }, + options: { relationshipIdOptions: this.relationshipOptions } }, - options: { relationshipIdOptions: this.relationshipOptions } - }, - width: '640px', maxWidth: '95vw', panelClass: 'fm-dialog-panel' - }); - }); + width: '640px', + maxWidth: '95vw', + panelClass: 'fm-dialog-panel' + }); + } + ); } editNominee(nominee: any, index: number): void { @@ -424,10 +510,12 @@ export class NomineeTabComponent implements OnInit { this.editNomType = nominee.nominationType || 'DA1'; } - cancelEdit(): void { this.editingIndex = null; } + cancelEdit(): void { + this.editingIndex = null; + } maxShareForEdit(index: number): number { - return 100 - this.nominees.reduce((sum, n, i) => i === index ? sum : sum + (n.percentageShare || 0), 0); + return 100 - this.nominees.reduce((sum, n, i) => (i === index ? sum : sum + (n.percentageShare || 0)), 0); } saveEdit(nominee: any, index: number): void { @@ -468,7 +556,8 @@ export class NomineeTabComponent implements OnInit { this.dialog.open(ImageZoomDialogComponent, { data: { imageSrc, title }, panelClass: 'image-zoom-panel', - maxWidth: '90vw', maxHeight: '90vh' + maxWidth: '90vw', + maxHeight: '90vh' }); } diff --git a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html index 6b22ded4d..6f4eca523 100644 --- a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html +++ b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html @@ -7,7 +7,6 @@ -->
-
@@ -50,13 +49,15 @@ @if (clientViewData.dateOfBirth) {
{{ 'labels.inputs.Date of Birth' | translate }}
-
{{ clientViewData.dateOfBirth | dateFormat:'DD-MMMM-YYYY' }}
+
{{ clientViewData.dateOfBirth | dateFormat: 'DD-MMMM-YYYY' }}
} @if (clientViewData.ageYears != null) {
Age
-
{{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d
+
+ {{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d +
} @if (clientViewData.gender) { @@ -75,7 +76,11 @@
Minor
- + {{ clientViewData.isMinor ? 'Yes (Age < 10)' : 'No' }}
@@ -84,7 +89,11 @@
{{ 'labels.inputs.Is staff' | translate }}
- + {{ clientViewData.isStaff ? 'Yes' : 'No' }}
@@ -100,7 +109,7 @@ @if (clientViewData.dateOfBirth) {
{{ 'labels.inputs.Incorporation Date' | translate }}
-
{{ clientViewData.dateOfBirth | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.dateOfBirth | dateFormat: 'DD-MM-YYYY' }}
} } @@ -112,7 +121,9 @@ }
Nationality
-
{{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }}
+
+ {{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }} +
@@ -176,7 +187,9 @@ @if (clientViewData.clientNonPersonDetails.incorpValidityTillDate) {
{{ 'labels.inputs.Incorporation Validity Till Date' | translate }}
-
{{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat:'DD-MM-YYYY' }}
+
+ {{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat: 'DD-MM-YYYY' }} +
} @if (clientViewData.clientNonPersonDetails.incorpNumber) { @@ -221,7 +234,9 @@
{{ 'labels.inputs.Email' | translate }}
- {{ clientViewData.emailAddress || '-' }} + {{ clientViewData.emailAddress || '-' }}
@@ -240,15 +255,19 @@
Mobile DIP Status
- @if (clientViewData.mobileVerified === true || clientViewData.mobileVerified === 'true' || clientViewData.mobileVerified === 1 - || clientViewData.dipVerified === true || clientViewData.dipVerified === 'true' || clientViewData.dipVerified === 1) { + @if ( + clientViewData.mobileVerified === true || + clientViewData.mobileVerified === 'true' || + clientViewData.mobileVerified === 1 || + clientViewData.dipVerified === true || + clientViewData.dipVerified === 'true' || + clientViewData.dipVerified === 1 + ) { Verified } @else { - - Pending - + Pending }
@@ -270,14 +289,16 @@
Alternate Mobile DIP Status
- @if (clientViewData.alternateDipVerified === true || clientViewData.alternateDipVerified === 'true' || clientViewData.alternateDipVerified === 1) { + @if ( + clientViewData.alternateDipVerified === true || + clientViewData.alternateDipVerified === 'true' || + clientViewData.alternateDipVerified === 1 + ) { Verified } @else { - - Pending - + Pending }
@@ -299,10 +320,20 @@
Name Screening (Sanctions)
- @if (clientViewData.nameVerified === true || clientViewData.nameVerified === 'true' || clientViewData.nameVerified === 1) { + @if ( + clientViewData.nameVerified === true || + clientViewData.nameVerified === 'true' || + clientViewData.nameVerified === 1 + ) { Clear - } @else if (clientViewData.nameVerified === false || clientViewData.nameVerified === 'false' || clientViewData.nameVerified === 0) { - Match Found + } @else if ( + clientViewData.nameVerified === false || + clientViewData.nameVerified === 'false' || + clientViewData.nameVerified === 0 + ) { + Match Found } @else { Pending } @@ -320,60 +351,60 @@ @if (clientViewData?.legalForm?.id !== 2) { -
-
- -

Financial & Classification

-
-
-
-
-
Monthly Income
-
- @if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') { - ₹ {{ clientViewData.monthlyIncome | number }} - } @else { - - - } +
+
+ +

Financial & Classification

+
+
+
+
+
Monthly Income
+
+ @if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') { + ₹ {{ clientViewData.monthlyIncome | number }} + } @else { + - + } +
+
+
Annual Turnover (Last FY)
+
+ @if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') { + ₹ {{ clientViewData.annualTurnover | number }} + } @else { + - + } +
+
+ @if (clientViewData.clientType) { +
+
{{ 'labels.inputs.Client Type' | translate }}
+
{{ clientViewData.clientType.name }}
+
+ } + @if (clientViewData.clientClassification) { +
+
{{ 'labels.inputs.Client Classification' | translate }}
+
{{ clientViewData.clientClassification.name }}
+
+ } + @if (clientViewData.occupationCategory) { +
+
{{ 'labels.inputs.Occupation Category' | translate }}
+
{{ clientViewData.occupationCategory.name }}
+
+ } + @if (clientViewData.occupation) { +
+
{{ 'labels.inputs.Occupation' | translate }}
+
{{ clientViewData.occupation.name }}
+
+ }
-
-
Annual Turnover (Last FY)
-
- @if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') { - ₹ {{ clientViewData.annualTurnover | number }} - } @else { - - - } -
-
- @if (clientViewData.clientType) { -
-
{{ 'labels.inputs.Client Type' | translate }}
-
{{ clientViewData.clientType.name }}
-
- } - @if (clientViewData.clientClassification) { -
-
{{ 'labels.inputs.Client Classification' | translate }}
-
{{ clientViewData.clientClassification.name }}
-
- } - @if (clientViewData.occupationCategory) { -
-
{{ 'labels.inputs.Occupation Category' | translate }}
-
{{ clientViewData.occupationCategory.name }}
-
- } - @if (clientViewData.occupation) { -
-
{{ 'labels.inputs.Occupation' | translate }}
-
{{ clientViewData.occupation.name }}
-
- }
-
} @@ -436,19 +467,19 @@ @if (clientViewData.timeline?.submittedOnDate) {
{{ 'labels.inputs.Submitted On' | translate }}
-
{{ clientViewData.timeline.submittedOnDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.timeline.submittedOnDate | dateFormat: 'DD-MM-YYYY' }}
} @if (clientViewData.activationDate) {
{{ 'labels.inputs.Activation Date' | translate }}
-
{{ clientViewData.activationDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.activationDate | dateFormat: 'DD-MM-YYYY' }}
} @if (clientViewData.timeline?.closedOnDate) {
{{ 'labels.inputs.Closed Date' | translate }}
-
{{ clientViewData.timeline.closedOnDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.timeline.closedOnDate | dateFormat: 'DD-MM-YYYY' }}
}
@@ -467,10 +498,14 @@
{{ 'labels.inputs.Client Status' | translate }}
- + [class.status-pending]=" + clientViewData.status.value !== 'Active' && clientViewData.status.value !== 'Closed' + " + > {{ 'labels.status.' + clientViewData.status.value | translate }}
@@ -485,5 +520,4 @@
-
diff --git a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.scss b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.scss index abd46cba7..1ca6f0ca9 100644 --- a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.scss +++ b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.scss @@ -6,6 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +@use 'sass:color'; + $primary: #1a73e8; $primary-light: #e8f0fe; $gray-50: #f8f9fa; @@ -83,15 +85,15 @@ $teal: #00897b; grid-template-columns: repeat(4, 1fr); gap: 0; - @media (max-width: 1024px) { + @media (width <= 1024px) { grid-template-columns: repeat(3, 1fr); } - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: repeat(2, 1fr); } - @media (max-width: 480px) { + @media (width <= 480px) { grid-template-columns: 1fr; } } @@ -165,7 +167,7 @@ $teal: #00897b; &.status-active { background: rgba($green, 0.1); - color: darken($green, 10%); + color: color.adjust($green, $lightness: -10%); &::before { background: $green; @@ -183,7 +185,7 @@ $teal: #00897b; &.status-pending { background: rgba($orange, 0.1); - color: darken($orange, 15%); + color: color.adjust($orange, $lightness: -15%); &::before { background: $orange; @@ -192,7 +194,7 @@ $teal: #00897b; } &.monospace { - font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; font-size: 13px; letter-spacing: 0.3px; } @@ -230,7 +232,9 @@ $teal: #00897b; padding: 3px 12px; border-radius: 12px; - i { font-size: 12px; } + i { + font-size: 12px; + } &--verified { background: #ecfdf5; diff --git a/src/app/clients/clients.component.html b/src/app/clients/clients.component.html index 164850b67..3e4826ee0 100644 --- a/src/app/clients/clients.component.html +++ b/src/app/clients/clients.component.html @@ -9,28 +9,14 @@
-
-

-
- {{ 'labels.menus.Clients' | translate }} -

- {{ totalRows }} records -
- -
- - - - - @if (existsClientsToFilter) { - - {{ 'labels.inputs.Show Closed Accounts' | translate }} - - } +
+
+

+
+ {{ 'labels.menus.Clients' | translate }} +

+ {{ totalRows }} records +
+ +
+ + + + + + + All Status + Active + Pending + Closed + + + + + + All Forms + Individual + Non-Individual + + + + + + All CKYC + CKYC Done + CKYC Pending + + +
@@ -67,8 +88,13 @@ {{ 'labels.inputs.name' | translate }} - {{ row.legalForm?.id === 2 ? 'business' : 'person' }} + {{ + row.legalForm?.id === 2 ? 'business' : 'person' + }} {{ hideClientData ? maskName(row.displayName) : row.displayName }} + @if (row._ckycDone) { + verified + } @@ -86,6 +112,18 @@ {{ row.cifNumber }} + + + CKYC Number + + @if (row._ckycNumber) { + {{ row._ckycNumber }} + } @else { + — + } + + + {{ 'labels.inputs.Legal Form' | translate }} @@ -112,7 +150,11 @@
@for (step of getProfileSteps(row); track step.label) { - + {{ step.label.charAt(0) }} } diff --git a/src/app/clients/clients.component.ts b/src/app/clients/clients.component.ts index 6eab7aadf..8399d538f 100644 --- a/src/app/clients/clients.component.ts +++ b/src/app/clients/clients.component.ts @@ -8,7 +8,6 @@ /** Angular Imports. */ import { Component, OnInit, ViewChild, inject } from '@angular/core'; -import { MatCheckbox } from '@angular/material/checkbox'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort, Sort, MatSortHeader } from '@angular/material/sort'; import { @@ -46,7 +45,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; styleUrls: ['./clients.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, - MatCheckbox, FaIconComponent, MatProgressBar, MatTable, @@ -88,12 +86,11 @@ export class ClientsComponent implements OnInit { .join(' '); } - @ViewChild('showClosedAccounts') showClosedAccounts: MatCheckbox; - displayedColumns = [ 'displayName', 'accountNumber', 'cifNumber', + 'ckycNumber', 'legalForm', 'status', 'profileCompletion', @@ -115,6 +112,12 @@ export class ClientsComponent implements OnInit { sortAttribute = ''; sortDirection = ''; + statusFilter = ''; + legalFormFilter = ''; + ckycFilter = ''; + + private allRows: any[] = []; + @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; @@ -139,14 +142,16 @@ export class ClientsComponent implements OnInit { .searchByText(this.filterText, this.currentPage, this.pageSize, this.sortAttribute, this.sortDirection) .subscribe( (data: any) => { - this.dataSource.data = data.content; + this.allRows = data.content || []; this.totalRows = data.totalElements; this.existsClientsToFilter = data.numberOfElements > 0; this.notExistsClientsToFilter = !this.existsClientsToFilter; this.isLoading = false; + this.applyClientFilters(); + // Fetch KYC & address status for visible rows - this.loadProfileStatus(data.content); + this.loadProfileStatus(this.allRows); }, (error: any) => { this.isLoading = false; @@ -162,35 +167,66 @@ export class ClientsComponent implements OnInit { // Use individual subscriptions instead of forkJoin to avoid EMPTY observable issues clients.forEach((client, i) => { - this.clientService.getClientData(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((data: any) => { - if (data?.legalForm) { - clients[i].legalForm = data.legalForm; - this.dataSource.data = [...clients]; - } - }); + this.clientService + .getClientData(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((data: any) => { + if (data?.legalForm) { + clients[i].legalForm = data.legalForm; + this.applyClientFilters(); + } + }); - this.clientService.getClientKyc(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((kyc: any) => { - if (kyc) { - clients[i]._kycDone = kyc.kycStatus === 300; - this.dataSource.data = [...clients]; - } - }); + this.clientService + .getClientKyc(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((kyc: any) => { + if (kyc) { + clients[i]._kycDone = kyc.kycStatus === 300; + clients[i]._ckycDone = !!kyc.ckycNumber; + clients[i]._ckycNumber = kyc.ckycNumber || ''; + this.applyClientFilters(); + } + }); - this.clientService.getClientAddressData(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((addr: any) => { - if (addr && Array.isArray(addr) && addr.length > 0) { - clients[i]._addressDone = true; - this.dataSource.data = [...clients]; - } - }); + this.clientService + .getClientAddressData(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((addr: any) => { + if (addr && Array.isArray(addr) && addr.length > 0) { + clients[i]._addressDone = true; + this.applyClientFilters(); + } + }); }); } + applyClientFilters() { + const status = this.statusFilter; + const legal = this.legalFormFilter; + const ckyc = this.ckycFilter; + + const filtered = (this.allRows || []).filter((row) => { + const code = (row?.status?.code || '').toLowerCase(); + + if (status && !code.includes(status)) { + return false; + } + if (legal && String(row?.legalForm?.id ?? '') !== legal) { + return false; + } + if (ckyc === 'done' && !row._ckycDone) { + return false; + } + if (ckyc === 'pending' && row._ckycDone) { + return false; + } + return true; + }); + + this.dataSource.data = filtered; + } + pageChanged(event: PageEvent) { this.pageSize = event.pageSize; this.currentPage = event.pageIndex; @@ -222,7 +258,7 @@ export class ClientsComponent implements OnInit { } getCompletedCount(row: any): number { - return this.getProfileSteps(row).filter(s => s.done).length; + return this.getProfileSteps(row).filter((s) => s.done).length; } private resetPaginator() { diff --git a/src/app/clients/clients.service.ts b/src/app/clients/clients.service.ts index 75299705d..de9b603a2 100644 --- a/src/app/clients/clients.service.ts +++ b/src/app/clients/clients.service.ts @@ -320,6 +320,94 @@ export class ClientsService { return this.http.get(`/clients/${clientId}/identifiers`); } + getKycProofTypes(type?: 'id' | 'address'): Observable { + const options = type ? { params: new HttpParams().set('type', type) } : {}; + return this.http.get(`/kyc/proof-types`, options); + } + + /** + * Phase 1 – Record Master: fetch clients missing CKYC registration + * or artefacts. Backed by GET /ckyc/record-master. + */ + getCkycRecordMaster( + filters: { + officeId?: number | string; + productId?: number | string; + fromDate?: string; + toDate?: string; + missing?: 'photo' | 'signature' | 'id' | 'address' | 'any'; + limit?: number; + offset?: number; + } = {} + ): Observable { + let params = new HttpParams(); + Object.entries(filters).forEach( + ([ + key, + value + ]) => { + if (value !== null && value !== undefined && value !== '') { + params = params.set(key, String(value)); + } + } + ); + return this.http.get(`/ckyc/record-master`, { params }); + } + + /** + * Phase 2 – Auto ZIP Creation: bundle selected clients into a CERSAI + * ZIP on the server. Backed by POST /ckyc/batches. + */ + createCkycBatch(body: { clientIds: (number | string)[]; remarks?: string }): Observable { + return this.http.post(`/ckyc/batches`, body); + } + + /** + * Phase 2/3 – List CKYC batches. Supports filters and an optional + * groupByDate=true to bucket results by created_on for the UI. + */ + getCkycBatches( + filters: { + fromDate?: string; + toDate?: string; + uploadedToCersai?: boolean; + groupByDate?: boolean; + } = {} + ): Observable { + let params = new HttpParams(); + Object.entries(filters).forEach( + ([ + key, + value + ]) => { + if (value !== null && value !== undefined && value !== '') { + params = params.set(key, String(value)); + } + } + ); + return this.http.get(`/ckyc/batches`, { params }); + } + + /** Fetch a single CKYC batch with parsed manifest. */ + getCkycBatchById(id: number | string): Observable { + return this.http.get(`/ckyc/batches/${id}`); + } + + /** Download the ZIP attached to a batch (via generic document endpoint). */ + downloadCkycBatchZip(parentEntityId: number | string, documentId: number | string): Observable { + return this.http.get(`/ckyc_batch/${parentEntityId}/documents/${documentId}/attachment`, { + responseType: 'blob' + }); + } + + /** + * Phase 3 – SFTP upload to CERSAI. Uploads the batch ZIP over the + * SFTP channel and returns the ACK + per-client CKYC numbers. + */ + uploadCkycBatchToSftp(batchId: number | string): Observable { + return this.http.post(`/ckyc/batches/${batchId}/sftp-upload`, {}); + } + getClientIdentifierTemplate(clientId: string) { return this.http.get(`/clients/${clientId}/identifiers/template`); } @@ -653,6 +741,29 @@ export class ClientsService { return this.http.post(`/clients/${clientId}/kyc/${kycId}?command=verify-gstin`, {}); } + /** + * Verifies DIN (Director Identification Number) for an authorized signatory. + * Sends signatory clientId, DIN number and the non-individual entity name. + * Backend saves DIN & verification status in KYC table and returns company list. + * @param clientId Signatory's Client ID + * @param kycId KYC record ID + * @param dinNumber DIN number to verify + * @param entityName Non-individual entity name (for company match) + */ + verifyDIN(clientId: string, kycId: string, dinNumber: string, entityName: string): Observable { + return this.http.post(`/clients/${clientId}/kyc/${kycId}?command=verify-din`, { dinNumber, entityName }); + } + + /** + * Completes Re-KYC after uploading signed self-declaration form. + * Updates Last Verified date to today and sets new Expiry Date (today + 10 years for low risk). + * @param clientId Client ID + * @param kycId KYC record ID + */ + completeReKyc(clientId: string, kycId: string): Observable { + return this.http.post(`/clients/${clientId}/kyc/${kycId}?command=complete-rekyc`, {}); + } + // ═══ Entity Classification APIs ═══ getEntityClassifications(clientId: string): Observable { diff --git a/src/app/clients/create-client/create-client.component.html b/src/app/clients/create-client/create-client.component.html index cf32712fa..3e60e8cd2 100644 --- a/src/app/clients/create-client/create-client.component.html +++ b/src/app/clients/create-client/create-client.component.html @@ -12,7 +12,12 @@

{{ 'labels.heading.Create Client' | translate }}

- + @@ -62,7 +67,8 @@
}
- edit Edit } @else { - }
} -
@@ -155,13 +166,18 @@ edit Edit } @else { - }
} - -
`, - 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] + 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 {} diff --git a/src/app/core/shell/shell.component.html b/src/app/core/shell/shell.component.html index 503471f30..ef418e772 100644 --- a/src/app/core/shell/shell.component.html +++ b/src/app/core/shell/shell.component.html @@ -27,22 +27,29 @@ @if ((isHandset$ | async) === false) { - + } @@ -56,7 +63,12 @@ Institution - + Tasks @@ -70,11 +82,21 @@ Admin - + Sub Ledger - + General Ledger @@ -121,7 +143,12 @@ Insurance - + RBI diff --git a/src/app/core/shell/sidenav/sidenav.component.html b/src/app/core/shell/sidenav/sidenav.component.html index 6fbe65754..06c2ebed5 100644 --- a/src/app/core/shell/sidenav/sidenav.component.html +++ b/src/app/core/shell/sidenav/sidenav.component.html @@ -65,6 +65,18 @@ {{ 'labels.menus.CKYC' | translate }} + + + + + KYC Management + {{ 'labels.menus.Individual Collection Sheet' | translate }} - - - - - {{ 'labels.menus.Remittances' | translate }} - + + + + + {{ 'labels.menus.Remittances' | translate }} + - - - - - {{ 'labels.menus.Analytics' | translate }} - + + + + + {{ 'labels.menus.Analytics' | translate }} + - - - - - - {{ 'labels.menus.CFCFRMS Dashboard' | translate }} - - - - - - {{ 'labels.menus.Complaints' | translate }} - - - - - - {{ 'labels.menus.Escalate' | translate }} - - - - - - {{ 'labels.menus.Report Suspect' | translate }} - - - - - - {{ 'labels.menus.Report Mule' | translate }} - - - - - - {{ 'labels.menus.Report Fraud Txn' | translate }} - + + + + + + {{ 'labels.menus.CFCFRMS Dashboard' | translate }} + + + + + + {{ 'labels.menus.Complaints' | translate }} + + + + + + {{ 'labels.menus.Escalate' | translate }} + + + + + + {{ 'labels.menus.Report Suspect' | translate }} + + + + + + {{ 'labels.menus.Report Mule' | translate }} + + + + + + {{ 'labels.menus.Report Fraud Txn' | translate }} + - - - - - - {{ 'labels.menus.FMR Submission' | translate }} - - - - - - {{ 'labels.menus.CFR Lookup' | translate }} - + + + + + + {{ 'labels.menus.FMR Submission' | translate }} + + + + + + {{ 'labels.menus.CFR Lookup' | translate }} +
@@ -66,7 +83,11 @@ @@ -364,7 +445,11 @@ -
@@ -377,7 +462,11 @@ - - - @@ -71,7 +77,12 @@ - + Q3 FY 2025-26 Reporting Quarter
- + RBI Circulars @@ -111,13 +117,19 @@ {{ item.code }} - + {{ item.category }} {{ item.prev }} {{ item.curr }} - {{ item.change }} + + {{ item.change }} + } @@ -159,7 +171,9 @@ {{ item.code }} {{ item.prev }} {{ item.curr }} - {{ item.change }} + + {{ item.change }} + } @@ -199,7 +213,9 @@
{{ item.sub }}
{{ item.code }} - {{ item.rw }} + + {{ item.rw }} + {{ item.gross }} {{ item.rwa }} @@ -240,7 +256,9 @@
{{ item.sub }}
{{ item.code }} - {{ item.approach }} + + {{ item.approach }} + {{ item.cc }} {{ item.rwa }} @@ -276,7 +294,7 @@
Gross Income Average (3-year)
Average of last 3 years positive gross income
- ₹{{ opRiskGrossIncome | number:'1.2-2' }} Cr + ₹{{ opRiskGrossIncome | number: '1.2-2' }} Cr 02 @@ -292,7 +310,7 @@
Capital Charge
Gross Income × Alpha
- ₹{{ opRiskCapitalCharge | number:'1.2-2' }} Cr + ₹{{ opRiskCapitalCharge | number: '1.2-2' }} Cr 04 @@ -300,7 +318,7 @@
RWA (Capital Charge × 12.5)
{{ opRiskCapitalCharge }} × 12.5 = {{ opRiskRWA }}
- ₹{{ opRiskRWA | number:'1.2-2' }} Cr + ₹{{ opRiskRWA | number: '1.2-2' }} Cr @@ -319,25 +337,25 @@
Tier 1 Capital
-
₹{{ totalTier1 | number:'1.2-2' }}
+
₹{{ totalTier1 | number: '1.2-2' }}
CET1 + AT1
+
Tier 2 Capital
-
₹{{ totalTier2 | number:'1.2-2' }}
+
₹{{ totalTier2 | number: '1.2-2' }}
Supplementary
÷
Total RWA
-
₹{{ totalRWA | number:'1.0-0' }}
+
₹{{ totalRWA | number: '1.0-0' }}
Credit + Market + Op
×
-
-
100
-
100
+
+
100
+
100
 
=
@@ -353,7 +371,10 @@
Basel III Compliance Status
-
All thresholds met · CRAR: {{ crar }}% · CET1: {{ cet1Ratio }}% · Tier 1: {{ tier1Ratio }}% · Tier 2: {{ tier2Ratio }}%
+
+ All thresholds met · CRAR: {{ crar }}% · CET1: {{ cet1Ratio }}% · Tier 1: {{ tier1Ratio }}% + · Tier 2: {{ tier2Ratio }}% +
diff --git a/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss b/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss index 17b254499..a80b2411c 100644 --- a/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss +++ b/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss @@ -198,12 +198,29 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; } } -.sc-t1c::before { background: $t1c; } -.sc-t2c::before { background: $t2c; } -.sc-total-cap::before { background: linear-gradient(90deg, $t1c, $t2c); } -.sc-rwa::before { background: $rwa; } -.sc-crar::before { background: $crar; } -.sc-buffer::before { background: $green; } +.sc-t1c::before { + background: $t1c; +} + +.sc-t2c::before { + background: $t2c; +} + +.sc-total-cap::before { + background: linear-gradient(90deg, $t1c, $t2c); +} + +.sc-rwa::before { + background: $rwa; +} + +.sc-crar::before { + background: $crar; +} + +.sc-buffer::before { + background: $green; +} .sc-label { font-size: 10px; @@ -239,8 +256,13 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; margin-top: 4px; } -.up { color: $green; } -.dn { color: $red; } +.up { + color: $green; +} + +.dn { + color: $red; +} // ===== Tab Navigation ===== .tab-nav { @@ -291,11 +313,35 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; letter-spacing: 0.5px; } -.pill-t1c { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.pill-t2c { background: $t2c-bg; color: $t2c; border: 1px solid $t2c-b; } -.pill-cr { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.pill-mr { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.pill-or { background: $or-bg; color: $or; border: 1px solid $or-b; } +.pill-t1c { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.pill-t2c { + background: $t2c-bg; + color: $t2c; + border: 1px solid $t2c-b; +} + +.pill-cr { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.pill-mr { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.pill-or { + background: $or-bg; + color: $or; + border: 1px solid $or-b; +} .section-desc { font-size: 12px; @@ -335,15 +381,22 @@ thead th { text-transform: uppercase; white-space: nowrap; - &.right { text-align: right; } + &.right { + text-align: right; + } } tbody tr { border-bottom: 1px solid $border; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } td { @@ -351,7 +404,10 @@ td { color: $ink2; vertical-align: middle; - &.right { text-align: right; font-family: $mono; } + &.right { + text-align: right; + font-family: $mono; + } } .sl { @@ -361,7 +417,10 @@ td { width: 36px; } -.mono { font-family: $mono; font-size: 12px; } +.mono { + font-family: $mono; + font-size: 12px; +} .item-name { font-weight: 500; @@ -379,12 +438,29 @@ td { font-weight: 500; } -.amt-t1c { color: $t1c; } -.amt-t2c { color: $t2c; } -.amt-cr { color: $cr; } -.amt-mr { color: $mr; } -.amt-or { color: $or; } -.amt-crar { color: $crar; } +.amt-t1c { + color: $t1c; +} + +.amt-t2c { + color: $t2c; +} + +.amt-cr { + color: $cr; +} + +.amt-mr { + color: $mr; +} + +.amt-or { + color: $or; +} + +.amt-crar { + color: $crar; +} .status-chip { display: inline-flex; @@ -397,17 +473,70 @@ td { border-radius: 4px; } -.chip-cet1 { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.chip-at1 { background: $rwa-bg; color: $rwa; border: 1px solid $rwa-b; } -.chip-rw { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.chip-approach { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.chip-active { background: #dcfce7; color: #15803d; } -.chip-t1c { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.chip-t2c { background: $t2c-bg; color: $t2c; border: 1px solid $t2c-b; } -.chip-cr { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.chip-mr { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.chip-or { background: $or-bg; color: $or; border: 1px solid $or-b; } -.chip-crar { background: $crar-bg; color: $crar; border: 1px solid $crar-b; } +.chip-cet1 { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.chip-at1 { + background: $rwa-bg; + color: $rwa; + border: 1px solid $rwa-b; +} + +.chip-rw { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.chip-approach { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.chip-active { + background: #dcfce7; + color: #15803d; +} + +.chip-t1c { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.chip-t2c { + background: $t2c-bg; + color: $t2c; + border: 1px solid $t2c-b; +} + +.chip-cr { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.chip-mr { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.chip-or { + background: $or-bg; + color: $or; + border: 1px solid $or-b; +} + +.chip-crar { + background: $crar-bg; + color: $crar; + border: 1px solid $crar-b; +} tfoot tr { background: $surface2; @@ -420,15 +549,35 @@ tfoot td { font-family: $mono; padding: 12px 14px; - &.right { text-align: right; font-size: 14px; } + &.right { + text-align: right; + font-size: 14px; + } } -.foot-t1c { color: $t1c; } -.foot-t2c { color: $t2c; } -.foot-cr { color: $cr; } -.foot-mr { color: $mr; } -.foot-or { color: $or; } -.foot-crar { color: $crar; } +.foot-t1c { + color: $t1c; +} + +.foot-t2c { + color: $t2c; +} + +.foot-cr { + color: $cr; +} + +.foot-mr { + color: $mr; +} + +.foot-or { + color: $or; +} + +.foot-crar { + color: $crar; +} // ===== Formula Block ===== .formula-block { @@ -511,33 +660,58 @@ tfoot td { padding: 0 8px; flex-shrink: 0; - &.minus { color: $red; } - &.equals { color: $ink2; font-weight: 500; } + &.minus { + color: $red; + } + + &.equals { + color: $ink2; + font-weight: 500; + } } .ft-t1c { border-color: $t1c-b; background: $t1c-bg; - .formula-term-label, .formula-term-val { color: $t1c; } + + .formula-term-label, + .formula-term-val { + color: $t1c; + } } .ft-t2c { border-color: $t2c-b; background: $t2c-bg; - .formula-term-label, .formula-term-val { color: $t2c; } + + .formula-term-label, + .formula-term-val { + color: $t2c; + } } .ft-rwa { border-color: $rwa-b; background: $rwa-bg; - .formula-term-label, .formula-term-val { color: $rwa; } + + .formula-term-label, + .formula-term-val { + color: $rwa; + } } .ft-crar-result { border-color: $crar-b; background: linear-gradient(135deg, #f0fdf4, #ecfdf5); - .formula-term-label { color: #15803d; } - .formula-term-val { color: $crar; font-size: 20px; } + + .formula-term-label { + color: #15803d; + } + + .formula-term-val { + color: $crar; + font-size: 20px; + } } // ===== Compliance Bar ===== @@ -553,7 +727,9 @@ tfoot td { flex-wrap: wrap; } -.compliance-left { flex: 1; } +.compliance-left { + flex: 1; +} .compliance-title { font-size: 13px; @@ -572,7 +748,9 @@ tfoot td { gap: 24px; } -.compliance-calc { text-align: right; } +.compliance-calc { + text-align: right; +} .compliance-calc-label { font-size: 10px; @@ -587,7 +765,9 @@ tfoot td { color: #14532d; font-family: $mono; - &.surplus { color: $crar; } + &.surplus { + color: $crar; + } } // ===== Footer ===== diff --git a/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts b/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts index 675f13b83..a1e937346 100644 --- a/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts +++ b/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts @@ -35,9 +35,9 @@ export class CrarDialogComponent { activeTab: 'tier1' | 'tier2' | 'creditRisk' | 'marketRisk' | 'opRisk' = 'tier1'; /** Capital Adequacy computations */ - readonly totalTier1 = 8284.80; - readonly totalTier2 = 1556.00; - readonly totalCapital = 9840.80; + readonly totalTier1 = 8284.8; + readonly totalTier2 = 1556.0; + readonly totalCapital = 9840.8; readonly totalRWA = 58420; readonly crar = 16.84; readonly cet1Ratio = 12.42; @@ -46,52 +46,251 @@ export class CrarDialogComponent { readonly capitalBuffer = 6.34; /** Credit Risk RWA */ - readonly totalCreditRWA = 47820.40; + readonly totalCreditRWA = 47820.4; /** Market Risk RWA */ - readonly totalMarketRWA = 7005.00; + readonly totalMarketRWA = 7005.0; /** Operational Risk RWA */ - readonly totalOpRWA = 3595.60; + readonly totalOpRWA = 3595.6; tier1Capital = [ - { sl: '01', name: 'Paid-up Equity Share Capital', sub: 'Ordinary equity shares fully paid', code: '5001-ESC', category: 'CET1', prev: '1,200.00', curr: '1,200.00', change: '\u2014' }, - { sl: '02', name: 'Statutory Reserves (Sec 17)', sub: 'Reserves under Sec 17 of BR Act', code: '5002-SR', category: 'CET1', prev: '2,840.60', curr: '3,012.40', change: '+171.80', changeDir: 'up' }, - { sl: '03', name: 'Capital Reserve', sub: 'Non-distributable capital reserves', code: '5003-CR', category: 'CET1', prev: '680.20', curr: '720.80', change: '+40.60', changeDir: 'up' }, - { sl: '04', name: 'Share Premium Account', sub: 'Premium collected on equity issuance', code: '5004-SP', category: 'CET1', prev: '1,480.00', curr: '1,480.00', change: '\u2014' }, - { sl: '05', name: 'Retained Earnings (Current Year)', sub: 'Profit retained after dividend payout', code: '5005-RE', category: 'CET1', prev: '1,060.40', curr: '1,284.60', change: '+224.20', changeDir: 'up' }, - { sl: '06', name: 'PNCPS', sub: 'Perpetual Non-Cumulative Preference Shares', code: '5101-PNCPS', category: 'AT1', prev: '500.00', curr: '500.00', change: '\u2014' }, - { sl: '07', name: 'Perpetual Debt Instruments', sub: 'Basel III compliant perpetual bonds', code: '5102-PDI', category: 'AT1', prev: '800.00', curr: '800.00', change: '\u2014' }, - { sl: '08', name: 'Innovative PDI', sub: 'Innovative perpetual debt instruments', code: '5103-IPDI', category: 'AT1', prev: '200.00', curr: '200.00', change: '\u2014' } + { + sl: '01', + name: 'Paid-up Equity Share Capital', + sub: 'Ordinary equity shares fully paid', + code: '5001-ESC', + category: 'CET1', + prev: '1,200.00', + curr: '1,200.00', + change: '\u2014' + }, + { + sl: '02', + name: 'Statutory Reserves (Sec 17)', + sub: 'Reserves under Sec 17 of BR Act', + code: '5002-SR', + category: 'CET1', + prev: '2,840.60', + curr: '3,012.40', + change: '+171.80', + changeDir: 'up' + }, + { + sl: '03', + name: 'Capital Reserve', + sub: 'Non-distributable capital reserves', + code: '5003-CR', + category: 'CET1', + prev: '680.20', + curr: '720.80', + change: '+40.60', + changeDir: 'up' + }, + { + sl: '04', + name: 'Share Premium Account', + sub: 'Premium collected on equity issuance', + code: '5004-SP', + category: 'CET1', + prev: '1,480.00', + curr: '1,480.00', + change: '\u2014' + }, + { + sl: '05', + name: 'Retained Earnings (Current Year)', + sub: 'Profit retained after dividend payout', + code: '5005-RE', + category: 'CET1', + prev: '1,060.40', + curr: '1,284.60', + change: '+224.20', + changeDir: 'up' + }, + { + sl: '06', + name: 'PNCPS', + sub: 'Perpetual Non-Cumulative Preference Shares', + code: '5101-PNCPS', + category: 'AT1', + prev: '500.00', + curr: '500.00', + change: '\u2014' + }, + { + sl: '07', + name: 'Perpetual Debt Instruments', + sub: 'Basel III compliant perpetual bonds', + code: '5102-PDI', + category: 'AT1', + prev: '800.00', + curr: '800.00', + change: '\u2014' + }, + { + sl: '08', + name: 'Innovative PDI', + sub: 'Innovative perpetual debt instruments', + code: '5103-IPDI', + category: 'AT1', + prev: '200.00', + curr: '200.00', + change: '\u2014' + } ]; tier2Capital = [ - { sl: '01', name: 'Subordinated Debt', sub: 'Long-term subordinated bonds', code: '6001-SD', prev: '620.00', curr: '620.00', change: '\u2014' }, - { sl: '02', name: 'General Provisions & Loan Loss', sub: 'General provisions for standard assets', code: '6002-GP', prev: '480.40', curr: '520.60', change: '+40.20', changeDir: 'up' }, - { sl: '03', name: 'Revaluation Reserves', sub: 'Reserves from asset revaluation (55%)', code: '6003-RR', prev: '184.20', curr: '196.80', change: '+12.60', changeDir: 'up' }, - { sl: '04', name: 'Investment Fluctuation Reserve', sub: 'IFR as per RBI guidelines', code: '6004-IFR', prev: '120.00', curr: '142.60', change: '+22.60', changeDir: 'up' }, - { sl: '05', name: 'Upper Tier 2 Bonds', sub: 'Upper Tier 2 capital instruments', code: '6005-UTB', prev: '80.00', curr: '76.00', change: '-4.00', changeDir: 'dn' } + { + sl: '01', + name: 'Subordinated Debt', + sub: 'Long-term subordinated bonds', + code: '6001-SD', + prev: '620.00', + curr: '620.00', + change: '\u2014' + }, + { + sl: '02', + name: 'General Provisions & Loan Loss', + sub: 'General provisions for standard assets', + code: '6002-GP', + prev: '480.40', + curr: '520.60', + change: '+40.20', + changeDir: 'up' + }, + { + sl: '03', + name: 'Revaluation Reserves', + sub: 'Reserves from asset revaluation (55%)', + code: '6003-RR', + prev: '184.20', + curr: '196.80', + change: '+12.60', + changeDir: 'up' + }, + { + sl: '04', + name: 'Investment Fluctuation Reserve', + sub: 'IFR as per RBI guidelines', + code: '6004-IFR', + prev: '120.00', + curr: '142.60', + change: '+22.60', + changeDir: 'up' + }, + { + sl: '05', + name: 'Upper Tier 2 Bonds', + sub: 'Upper Tier 2 capital instruments', + code: '6005-UTB', + prev: '80.00', + curr: '76.00', + change: '-4.00', + changeDir: 'dn' + } ]; creditRiskRWA = [ - { sl: '01', name: 'Claims on Sovereign', sub: 'Central & State Government exposures', code: '7001-SOV', rw: '0%', gross: '18,420.60', rwa: '0.00' }, - { sl: '02', name: 'Claims on Banks', sub: 'Inter-bank exposures — scheduled banks', code: '7002-BNK', rw: '20%', gross: '4,210.40', rwa: '842.08' }, - { sl: '03', name: 'Corporate Loans Rated', sub: 'Rated corporate term loans', code: '7003-CORP', rw: '100%', gross: '12,840.20', rwa: '12,840.20' }, - { sl: '04', name: 'Retail Portfolio', sub: 'Retail loans — personal, auto, education', code: '7004-RTL', rw: '75%', gross: '8,640.80', rwa: '6,480.60' }, - { sl: '05', name: 'Housing Loans', sub: 'Home loans — LTV based risk weight', code: '7005-HLE', rw: '35%', gross: '6,820.40', rwa: '2,387.14' } + { + sl: '01', + name: 'Claims on Sovereign', + sub: 'Central & State Government exposures', + code: '7001-SOV', + rw: '0%', + gross: '18,420.60', + rwa: '0.00' + }, + { + sl: '02', + name: 'Claims on Banks', + sub: 'Inter-bank exposures — scheduled banks', + code: '7002-BNK', + rw: '20%', + gross: '4,210.40', + rwa: '842.08' + }, + { + sl: '03', + name: 'Corporate Loans Rated', + sub: 'Rated corporate term loans', + code: '7003-CORP', + rw: '100%', + gross: '12,840.20', + rwa: '12,840.20' + }, + { + sl: '04', + name: 'Retail Portfolio', + sub: 'Retail loans — personal, auto, education', + code: '7004-RTL', + rw: '75%', + gross: '8,640.80', + rwa: '6,480.60' + }, + { + sl: '05', + name: 'Housing Loans', + sub: 'Home loans — LTV based risk weight', + code: '7005-HLE', + rw: '35%', + gross: '6,820.40', + rwa: '2,387.14' + } ]; marketRiskRWA = [ - { sl: '01', name: 'Interest Rate Risk', sub: 'Duration & modified duration approach', code: '8001-IRR', approach: 'SDA', cc: '284.60', rwa: '3,557.50' }, - { sl: '02', name: 'Forex Risk', sub: 'Net open position in foreign currencies', code: '8002-FX', approach: 'Std', cc: '148.40', rwa: '1,855.00' }, - { sl: '03', name: 'Equity Risk', sub: 'Listed & unlisted equity exposures', code: '8003-EQR', approach: 'Std', cc: '84.20', rwa: '1,052.50' }, - { sl: '04', name: 'Commodity Price Risk', sub: 'Commodity position risk charges', code: '8004-CMR', approach: 'Simplified', cc: '24.80', rwa: '310.00' }, - { sl: '05', name: 'Options Risk', sub: 'Options portfolio — scenario analysis', code: '8005-OPR', approach: 'Scenario', cc: '18.40', rwa: '230.00' } + { + sl: '01', + name: 'Interest Rate Risk', + sub: 'Duration & modified duration approach', + code: '8001-IRR', + approach: 'SDA', + cc: '284.60', + rwa: '3,557.50' + }, + { + sl: '02', + name: 'Forex Risk', + sub: 'Net open position in foreign currencies', + code: '8002-FX', + approach: 'Std', + cc: '148.40', + rwa: '1,855.00' + }, + { + sl: '03', + name: 'Equity Risk', + sub: 'Listed & unlisted equity exposures', + code: '8003-EQR', + approach: 'Std', + cc: '84.20', + rwa: '1,052.50' + }, + { + sl: '04', + name: 'Commodity Price Risk', + sub: 'Commodity position risk charges', + code: '8004-CMR', + approach: 'Simplified', + cc: '24.80', + rwa: '310.00' + }, + { + sl: '05', + name: 'Options Risk', + sub: 'Options portfolio — scenario analysis', + code: '8005-OPR', + approach: 'Scenario', + cc: '18.40', + rwa: '230.00' + } ]; /** Operational Risk — Basic Indicator Approach */ - readonly opRiskGrossIncome = 2984.40; + readonly opRiskGrossIncome = 2984.4; readonly opRiskAlpha = 15; readonly opRiskCapitalCharge = 447.66; - readonly opRiskRWA = 3595.60; // 447.66 × 12.5 = 5595.75 — using provided value + readonly opRiskRWA = 3595.6; // 447.66 × 12.5 = 5595.75 — using provided value close(): void { this.dialogRef.close(); diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html index 6dc0b90ca..cf231ab89 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html @@ -22,7 +22,13 @@ {{ reportingDateStr }} Reporting Fortnight (45-day lag)
- + RBI Circulars @@ -56,8 +62,8 @@
CRR Required ({{ crrRate }}%)
-
₹{{ crrRequired | number:'1.0-0' }}Cr
-
SLR Req: ₹{{ slrRequired | number:'1.0-0' }} Cr ({{ slrRate }}%)
+
₹{{ crrRequired | number: '1.0-0' }}Cr
+
SLR Req: ₹{{ slrRequired | number: '1.0-0' }} Cr ({{ slrRate }}%)
@@ -104,11 +110,15 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} + + {{ item.change }} + - + [class.chip-overdue]="item.status === 'Overdue' || item.status === 'Unclaimed'" + > {{ item.status }} @@ -155,11 +165,15 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} + + {{ item.change }} + - + [class.chip-locked]="item.status === 'Locked'" + > {{ item.status }} @@ -206,14 +220,20 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} - {{ item.status }} + + {{ item.change }} + + + {{ item.status }} + } - Total Assets with Banking System ( Deducted from NDTL ) + + Total Assets with Banking System ( Deducted from NDTL ) + 7,600.70 8,240.20 +639.50 @@ -226,7 +246,9 @@ @if (activeTab === 'slr') { -
SLR = (Liquid Assets / NDTL) × 100 — RBI Sec 24(2A) of Banking Regulation Act, 1949
+
+ SLR = (Liquid Assets / NDTL) × 100 — RBI Sec 24(2A) of Banking Regulation Act, 1949 +
@@ -250,11 +272,17 @@
{{ item.sub }}
- + - - + + } @@ -286,9 +314,9 @@
Net Demand & Time
×
-
-
100
-
100
+
+
100
+
100
 
=
@@ -304,20 +332,25 @@
SLR Compliance Status
-
Actual: {{ slrActualPercent }}% · Required: {{ slrRate }}.00% · NDTL: ₹{{ ndtl | number:'1.2-2' }} Cr
+
+ Actual: {{ slrActualPercent }}% · Required: {{ slrRate }}.00% · NDTL: ₹{{ + ndtl | number: '1.2-2' + }} + Cr +
SLR Required ({{ slrRate }}%)
-
₹{{ slrRequired | number:'1.2-2' }} Cr
+
₹{{ slrRequired | number: '1.2-2' }} Cr
Liquid Assets Held
-
₹{{ totalLiquidAssets | number:'1.2-2' }} Cr
+
₹{{ totalLiquidAssets | number: '1.2-2' }} Cr
Surplus
-
₹{{ slrSurplus | number:'1.2-2' }} Cr
+
₹{{ slrSurplus | number: '1.2-2' }} Cr
@@ -325,29 +358,31 @@
-
NDTL Computation — Formula Breakdown · Data as of {{ reportingDateStr }}
+
+ NDTL Computation — Formula Breakdown · Data as of {{ reportingDateStr }} +
Demand Liabilities
-
₹{{ totalDemand | number:'1.2-2' }}
+
₹{{ totalDemand | number: '1.2-2' }}
16 line items
+
Time Liabilities
-
₹{{ totalTime | number:'1.2-2' }}
+
₹{{ totalTime | number: '1.2-2' }}
13 line items
−
Assets with Banking
-
₹{{ totalBankingAssets | number:'1.2-2' }}
+
₹{{ totalBankingAssets | number: '1.2-2' }}
3 line items
=
NDTL
-
₹{{ ndtl | number:'1.2-2' }}
+
₹{{ ndtl | number: '1.2-2' }}
CRR & SLR base
@@ -357,20 +392,22 @@
CRR & SLR Reserve Requirements — RBI Computation on NDTL
-
Based on NDTL of ₹{{ ndtl | number:'1.2-2' }} Cr · Data as of {{ reportingDateStr }} (45-day lag)
+
+ Based on NDTL of ₹{{ ndtl | number: '1.2-2' }} Cr · Data as of {{ reportingDateStr }} (45-day lag) +
CRR @ {{ crrRate }}.00%
-
₹{{ crrRequired | number:'1.2-2' }} Cr
+
₹{{ crrRequired | number: '1.2-2' }} Cr
SLR @ {{ slrRate }}.00%
-
₹{{ slrRequired | number:'1.2-2' }} Cr
+
₹{{ slrRequired | number: '1.2-2' }} Cr
Total Reserve
-
₹{{ totalReserve | number:'1.2-2' }} Cr
+
₹{{ totalReserve | number: '1.2-2' }} Cr
diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss index 002397f57..4db85bd22 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss @@ -186,11 +186,25 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; } } -.sc-demand::before { background: $demand; } -.sc-time::before { background: $time; } -.sc-asset::before { background: $asset; } -.sc-ndtl::before { background: linear-gradient(90deg, $asset, $time, $demand); } -.sc-crr::before { background: $amber; } +.sc-demand::before { + background: $demand; +} + +.sc-time::before { + background: $time; +} + +.sc-asset::before { + background: $asset; +} + +.sc-ndtl::before { + background: linear-gradient(90deg, $asset, $time, $demand); +} + +.sc-crr::before { + background: $amber; +} .sc-label { font-size: 10px; @@ -226,8 +240,13 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; margin-top: 4px; } -.up { color: $green; } -.dn { color: $red; } +.up { + color: $green; +} + +.dn { + color: $red; +} // ===== Tab Navigation ===== .tab-nav { @@ -278,9 +297,23 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; letter-spacing: 0.5px; } -.pill-demand { background: $demand-bg; color: $demand; border: 1px solid $demand-b; } -.pill-time { background: $time-bg; color: $time; border: 1px solid $time-b; } -.pill-asset { background: $asset-bg; color: $asset; border: 1px solid $asset-b; } +.pill-demand { + background: $demand-bg; + color: $demand; + border: 1px solid $demand-b; +} + +.pill-time { + background: $time-bg; + color: $time; + border: 1px solid $time-b; +} + +.pill-asset { + background: $asset-bg; + color: $asset; + border: 1px solid $asset-b; +} .section-desc { font-size: 12px; @@ -320,15 +353,22 @@ thead th { text-transform: uppercase; white-space: nowrap; - &.right { text-align: right; } + &.right { + text-align: right; + } } tbody tr { border-bottom: 1px solid $border; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } td { @@ -336,7 +376,10 @@ td { color: $ink2; vertical-align: middle; - &.right { text-align: right; font-family: $mono; } + &.right { + text-align: right; + font-family: $mono; + } } .sl { @@ -346,7 +389,10 @@ td { width: 36px; } -.mono { font-family: $mono; font-size: 12px; } +.mono { + font-family: $mono; + font-size: 12px; +} .item-name { font-weight: 500; @@ -364,9 +410,17 @@ td { font-weight: 500; } -.amt-demand { color: $demand; } -.amt-time { color: $time; } -.amt-asset { color: $asset; } +.amt-demand { + color: $demand; +} + +.amt-time { + color: $time; +} + +.amt-asset { + color: $asset; +} .status-chip { display: inline-flex; @@ -379,9 +433,20 @@ td { border-radius: 4px; } -.chip-active { background: #dcfce7; color: #15803d; } -.chip-overdue { background: #fef2f2; color: $red; } -.chip-locked { background: #fefce8; color: #92400e; } +.chip-active { + background: #dcfce7; + color: #15803d; +} + +.chip-overdue { + background: #fef2f2; + color: $red; +} + +.chip-locked { + background: #fefce8; + color: #92400e; +} tfoot tr { background: $surface2; @@ -394,34 +459,77 @@ tfoot td { font-family: $mono; padding: 12px 14px; - &.right { text-align: right; font-size: 14px; } + &.right { + text-align: right; + font-size: 14px; + } } $slr: #0369a1; $slr-bg: #f0f9ff; $slr-b: #bae6fd; -.foot-demand { color: $demand; } -.foot-time { color: $time; } -.foot-asset { color: $asset; } -.foot-slr { color: $slr; } -.deduct-tag { font-size: 10px; color: $asset; font-family: $mono; margin-left: 8px; } +.foot-demand { + color: $demand; +} -.amt-slr { color: $slr; } -.chip-slr { background: $slr-bg; color: $slr; border: 1px solid $slr-b; } -.pill-slr { background: $slr-bg; color: $slr; border: 1px solid $slr-b; } +.foot-time { + color: $time; +} + +.foot-asset { + color: $asset; +} + +.foot-slr { + color: $slr; +} + +.deduct-tag { + font-size: 10px; + color: $asset; + font-family: $mono; + margin-left: 8px; +} + +.amt-slr { + color: $slr; +} + +.chip-slr { + background: $slr-bg; + color: $slr; + border: 1px solid $slr-b; +} + +.pill-slr { + background: $slr-bg; + color: $slr; + border: 1px solid $slr-b; +} .ft-slr { border-color: $slr-b; background: $slr-bg; - .formula-term-label, .formula-term-val { color: $slr; } + + .formula-term-label, + .formula-term-val { + color: $slr; + } } .ft-slr-result { border-color: #bbf7d0; background: linear-gradient(135deg, #f0fdf4, #ecfdf5); - .formula-term-label { color: #15803d; } - .formula-term-val { color: $green; font-size: 20px; } + + .formula-term-label { + color: #15803d; + } + + .formula-term-val { + color: $green; + font-size: 20px; + } } .slr-formula::before { @@ -441,7 +549,9 @@ $slr-b: #bae6fd; flex-wrap: wrap; } -.slr-status-left { flex: 1; } +.slr-status-left { + flex: 1; +} .slr-status-title { font-size: 13px; @@ -460,7 +570,9 @@ $slr-b: #bae6fd; gap: 24px; } -.slr-calc { text-align: right; } +.slr-calc { + text-align: right; +} .slr-calc-label { font-size: 10px; @@ -475,7 +587,9 @@ $slr-b: #bae6fd; color: #14532d; font-family: $mono; - &.surplus { color: $green; } + &.surplus { + color: $green; + } } // ===== Formula Block ===== @@ -555,18 +669,58 @@ $slr-b: #bae6fd; padding: 0 8px; flex-shrink: 0; - &.minus { color: $red; } - &.equals { color: $ink2; font-weight: 500; } + &.minus { + color: $red; + } + + &.equals { + color: $ink2; + font-weight: 500; + } +} + +.ft-demand { + border-color: $demand-b; + background: $demand-bg; + + .formula-term-label, + .formula-term-val { + color: $demand; + } +} + +.ft-time { + border-color: $time-b; + background: $time-bg; + + .formula-term-label, + .formula-term-val { + color: $time; + } +} + +.ft-asset { + border-color: $asset-b; + background: $asset-bg; + + .formula-term-label, + .formula-term-val { + color: $asset; + } } -.ft-demand { border-color: $demand-b; background: $demand-bg; .formula-term-label, .formula-term-val { color: $demand; } } -.ft-time { border-color: $time-b; background: $time-bg; .formula-term-label, .formula-term-val { color: $time; } } -.ft-asset { border-color: $asset-b; background: $asset-bg; .formula-term-label, .formula-term-val { color: $asset; } } .ft-ndtl { border-color: #a78bfa; background: linear-gradient(135deg, #f5f3ff, #f0fdfa); - .formula-term-label { color: #6d28d9; } - .formula-term-val { color: $ink; font-size: 18px; } + + .formula-term-label { + color: #6d28d9; + } + + .formula-term-val { + color: $ink; + font-size: 18px; + } } // ===== CRR Bar ===== @@ -594,14 +748,18 @@ $slr-b: #bae6fd; color: #a16207; } -.crr-text { flex: 1; } +.crr-text { + flex: 1; +} .crr-calcs { display: flex; gap: 24px; } -.crr-calc { text-align: right; } +.crr-calc { + text-align: right; +} .crr-calc-label { font-size: 10px; diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts index ef230c679..7f7746251 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts @@ -40,18 +40,18 @@ export class CrrSlrDialogComponent { reportingDateStr: string; /** NDTL & Reserve computations */ - readonly totalDemand = 12843.50; - readonly totalTime = 64618.00; - readonly totalBankingAssets = 8240.20; - readonly ndtl = 69221.30; // Demand + Time - Banking Assets + readonly totalDemand = 12843.5; + readonly totalTime = 64618.0; + readonly totalBankingAssets = 8240.2; + readonly ndtl = 69221.3; // Demand + Time - Banking Assets readonly crrRate = 3; // Current RBI CRR rate (%) readonly slrRate = 18; // Current RBI SLR rate (%) - readonly crrRequired = 69221.30 * 0.03; // ₹2,076.64 Cr - readonly slrRequired = 69221.30 * 0.18; // ₹12,459.83 Cr - readonly totalLiquidAssets = 13238.80; - readonly slrActualPercent = +(13238.80 / 69221.30 * 100).toFixed(2); // 19.12% - readonly slrSurplus = +(13238.80 - 69221.30 * 0.18).toFixed(2); // 778.97 - readonly totalReserve = +(69221.30 * 0.03 + 69221.30 * 0.18).toFixed(2); // 14,536.47 + readonly crrRequired = 69221.3 * 0.03; // ₹2,076.64 Cr + readonly slrRequired = 69221.3 * 0.18; // ₹12,459.83 Cr + readonly totalLiquidAssets = 13238.8; + readonly slrActualPercent = +((13238.8 / 69221.3) * 100).toFixed(2); // 19.12% + readonly slrSurplus = +(13238.8 - 69221.3 * 0.18).toFixed(2); // 778.97 + readonly totalReserve = +(69221.3 * 0.03 + 69221.3 * 0.18).toFixed(2); // 14,536.47 constructor() { // CRR/SLR uses data from 45 days ago @@ -59,58 +59,446 @@ export class CrrSlrDialogComponent { this.reportingDate = new Date(this.today); this.reportingDate.setDate(this.reportingDate.getDate() - lagDays); this.reportingDateStr = this.reportingDate.toLocaleDateString('en-IN', { - day: '2-digit', month: 'short', year: 'numeric' + day: '2-digit', + month: 'short', + year: 'numeric' }); } demandLiabilities = [ - { sl: '01', name: 'Current Account Deposits', sub: 'Corporate & individual CA balances', code: '2001-CA', opening: '3,842.50', current: '4,120.75', change: '+278.25', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Savings Bank Deposits (Withdrawable)', sub: 'Demand portion — 10% of SB balance', code: '2002-SB', opening: '1,240.00', current: '1,318.40', change: '+78.40', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Demand Drafts Payable', sub: 'Issued DDs outstanding for payment', code: '2003-DD', opening: '284.20', current: '311.60', change: '+27.40', changeDir: 'up', status: 'Active' }, - { sl: '04', name: "Banker's Cheques / Pay Orders", sub: 'Issued pay orders pending encashment', code: '2004-PO', opening: '198.75', current: '176.30', change: '-22.45', changeDir: 'dn', status: 'Active' }, - { sl: '05', name: 'Credit Balance in Cash Credit Accounts', sub: 'CC accounts in credit position', code: '2005-CC', opening: '542.10', current: '618.90', change: '+76.80', changeDir: 'up', status: 'Active' }, - { sl: '06', name: 'Overdue Fixed Deposits', sub: 'FDs past maturity — treated as demand', code: '2006-OFD', opening: '89.40', current: '112.85', change: '+23.45', changeDir: 'dn', status: 'Overdue' }, - { sl: '07', name: 'Interest Payable on Demand Deposits', sub: 'Accrued interest on CA/SB', code: '2007-INT', opening: '34.20', current: '38.55', change: '+4.35', changeDir: 'up', status: 'Active' }, - { sl: '08', name: 'Margins held against Letters of Credit', sub: 'Withdrawable LC margin deposits', code: '2008-LCM', opening: '412.60', current: '388.20', change: '-24.40', changeDir: 'dn', status: 'Active' }, - { sl: '09', name: 'Margins held against Bank Guarantees', sub: 'Withdrawable BG margin deposits', code: '2009-BGM', opening: '318.40', current: '342.10', change: '+23.70', changeDir: 'up', status: 'Active' }, - { sl: '10', name: 'Security Deposits (Withdrawable)', sub: 'EMD / SD refundable on demand', code: '2010-SD', opening: '124.80', current: '118.60', change: '-6.20', changeDir: 'dn', status: 'Active' }, - { sl: '11', name: 'Unclaimed Deposits (Demand Portion)', sub: 'Balances unclaimed >10 years', code: '2011-UCD', opening: '56.30', current: '59.40', change: '+3.10', changeDir: 'dn', status: 'Unclaimed' }, - { sl: '12', name: 'Inter-Bank Demand Deposits', sub: 'Demand deposits from other banks', code: '2012-IBD', opening: '2,840.00', current: '3,120.50', change: '+280.50', changeDir: 'up', status: 'Active' }, - { sl: '13', name: 'Clearing Adjustments / Balances', sub: 'Net clearing settlement balances', code: '2013-CLR', opening: '186.40', current: '210.80', change: '+24.40', changeDir: 'up', status: 'Active' }, - { sl: '14', name: 'Outstanding Payment Instruments', sub: 'Cheques issued but not yet presented', code: '2014-OPI', opening: '142.60', current: '158.90', change: '+16.30', changeDir: 'up', status: 'Active' }, - { sl: '15', name: 'Demand Liabilities towards Other Banks', sub: 'Nostro / Vostro demand obligations', code: '2015-DLB', opening: '984.20', current: '1,048.60', change: '+64.40', changeDir: 'up', status: 'Active' }, - { sl: '16', name: 'Deposits Repayable on Demand', sub: 'Miscellaneous on-demand deposits', code: '2016-DRD', opening: '618.30', current: '698.05', change: '+79.75', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Current Account Deposits', + sub: 'Corporate & individual CA balances', + code: '2001-CA', + opening: '3,842.50', + current: '4,120.75', + change: '+278.25', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Savings Bank Deposits (Withdrawable)', + sub: 'Demand portion — 10% of SB balance', + code: '2002-SB', + opening: '1,240.00', + current: '1,318.40', + change: '+78.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Demand Drafts Payable', + sub: 'Issued DDs outstanding for payment', + code: '2003-DD', + opening: '284.20', + current: '311.60', + change: '+27.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '04', + name: "Banker's Cheques / Pay Orders", + sub: 'Issued pay orders pending encashment', + code: '2004-PO', + opening: '198.75', + current: '176.30', + change: '-22.45', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '05', + name: 'Credit Balance in Cash Credit Accounts', + sub: 'CC accounts in credit position', + code: '2005-CC', + opening: '542.10', + current: '618.90', + change: '+76.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '06', + name: 'Overdue Fixed Deposits', + sub: 'FDs past maturity — treated as demand', + code: '2006-OFD', + opening: '89.40', + current: '112.85', + change: '+23.45', + changeDir: 'dn', + status: 'Overdue' + }, + { + sl: '07', + name: 'Interest Payable on Demand Deposits', + sub: 'Accrued interest on CA/SB', + code: '2007-INT', + opening: '34.20', + current: '38.55', + change: '+4.35', + changeDir: 'up', + status: 'Active' + }, + { + sl: '08', + name: 'Margins held against Letters of Credit', + sub: 'Withdrawable LC margin deposits', + code: '2008-LCM', + opening: '412.60', + current: '388.20', + change: '-24.40', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '09', + name: 'Margins held against Bank Guarantees', + sub: 'Withdrawable BG margin deposits', + code: '2009-BGM', + opening: '318.40', + current: '342.10', + change: '+23.70', + changeDir: 'up', + status: 'Active' + }, + { + sl: '10', + name: 'Security Deposits (Withdrawable)', + sub: 'EMD / SD refundable on demand', + code: '2010-SD', + opening: '124.80', + current: '118.60', + change: '-6.20', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '11', + name: 'Unclaimed Deposits (Demand Portion)', + sub: 'Balances unclaimed >10 years', + code: '2011-UCD', + opening: '56.30', + current: '59.40', + change: '+3.10', + changeDir: 'dn', + status: 'Unclaimed' + }, + { + sl: '12', + name: 'Inter-Bank Demand Deposits', + sub: 'Demand deposits from other banks', + code: '2012-IBD', + opening: '2,840.00', + current: '3,120.50', + change: '+280.50', + changeDir: 'up', + status: 'Active' + }, + { + sl: '13', + name: 'Clearing Adjustments / Balances', + sub: 'Net clearing settlement balances', + code: '2013-CLR', + opening: '186.40', + current: '210.80', + change: '+24.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '14', + name: 'Outstanding Payment Instruments', + sub: 'Cheques issued but not yet presented', + code: '2014-OPI', + opening: '142.60', + current: '158.90', + change: '+16.30', + changeDir: 'up', + status: 'Active' + }, + { + sl: '15', + name: 'Demand Liabilities towards Other Banks', + sub: 'Nostro / Vostro demand obligations', + code: '2015-DLB', + opening: '984.20', + current: '1,048.60', + change: '+64.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '16', + name: 'Deposits Repayable on Demand', + sub: 'Miscellaneous on-demand deposits', + code: '2016-DRD', + opening: '618.30', + current: '698.05', + change: '+79.75', + changeDir: 'up', + status: 'Active' + } ]; timeLiabilities = [ - { sl: '01', name: 'Fixed Deposits (FD)', sub: '7 days to 10 years · 6.5–7.2%', code: '3001-FD', opening: '28,420.50', current: '29,184.30', change: '+763.80', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Recurring Deposits (RD)', sub: 'Monthly instalment · 6.0–6.8%', code: '3002-RD', opening: '4,218.60', current: '4,412.80', change: '+194.20', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Time Liability Portion of Savings', sub: '90% of SB balance treated as time', code: '3003-SBT', opening: '10,840.20', current: '11,204.60', change: '+364.40', changeDir: 'up', status: 'Active' }, - { sl: '04', name: 'Reinvestment Deposits', sub: 'Cumulative interest plans · 7.0%', code: '3004-RID', opening: '1,842.40', current: '1,918.70', change: '+76.30', changeDir: 'up', status: 'Active' }, - { sl: '05', name: 'Staff Security Deposits (Fixed Period)', sub: 'Employee security deposits — locked', code: '3005-SSD', opening: '84.60', current: '86.40', change: '+1.80', changeDir: 'up', status: 'Locked' }, - { sl: '06', name: 'Margin Money for Guarantees / LC', sub: 'LC/BG margins — fixed lock-in period', code: '3006-MGM', opening: '620.80', current: '644.20', change: '+23.40', changeDir: 'up', status: 'Locked' }, - { sl: '07', name: 'Certificates of Deposit (CD)', sub: 'Issued to corporates / FIs · 7.35%', code: '3007-CD', opening: '3,840.00', current: '3,920.50', change: '+80.50', changeDir: 'up', status: 'Active' }, - { sl: '08', name: 'Interest Accrued on Time Deposits', sub: 'Accrued but unpaid interest on FD/RD', code: '3008-IAT', opening: '842.60', current: '918.40', change: '+75.80', changeDir: 'up', status: 'Active' }, - { sl: '09', name: 'Long-term Deposits', sub: 'Tenor >5 years · 7.5%', code: '3009-LTD', opening: '5,620.40', current: '5,818.60', change: '+198.20', changeDir: 'up', status: 'Active' }, - { sl: '10', name: 'Tax Saving Deposits', sub: 'Sec 80C FDs — 5-year lock-in · 6.8%', code: '3010-TSD', opening: '1,240.80', current: '1,318.40', change: '+77.60', changeDir: 'up', status: 'Locked' }, - { sl: '11', name: 'Foreign Currency Deposits', sub: 'FCNR(B), RFC — USD/EUR/GBP', code: '3011-FCD', opening: '2,184.60', current: '2,348.20', change: '+163.60', changeDir: 'up', status: 'Active' }, - { sl: '12', name: 'Time Liabilities to Other Banks', sub: 'Inter-bank term deposits — call money', code: '3012-TLB', opening: '1,840.20', current: '1,924.40', change: '+84.20', changeDir: 'up', status: 'Active' }, - { sl: '13', name: 'Term Borrowings', sub: 'Bonds, debentures, refinance · 8.1%', code: '3013-TBR', opening: '1,018.40', current: '1,118.50', change: '+100.10', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Fixed Deposits (FD)', + sub: '7 days to 10 years · 6.5–7.2%', + code: '3001-FD', + opening: '28,420.50', + current: '29,184.30', + change: '+763.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Recurring Deposits (RD)', + sub: 'Monthly instalment · 6.0–6.8%', + code: '3002-RD', + opening: '4,218.60', + current: '4,412.80', + change: '+194.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Time Liability Portion of Savings', + sub: '90% of SB balance treated as time', + code: '3003-SBT', + opening: '10,840.20', + current: '11,204.60', + change: '+364.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '04', + name: 'Reinvestment Deposits', + sub: 'Cumulative interest plans · 7.0%', + code: '3004-RID', + opening: '1,842.40', + current: '1,918.70', + change: '+76.30', + changeDir: 'up', + status: 'Active' + }, + { + sl: '05', + name: 'Staff Security Deposits (Fixed Period)', + sub: 'Employee security deposits — locked', + code: '3005-SSD', + opening: '84.60', + current: '86.40', + change: '+1.80', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '06', + name: 'Margin Money for Guarantees / LC', + sub: 'LC/BG margins — fixed lock-in period', + code: '3006-MGM', + opening: '620.80', + current: '644.20', + change: '+23.40', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '07', + name: 'Certificates of Deposit (CD)', + sub: 'Issued to corporates / FIs · 7.35%', + code: '3007-CD', + opening: '3,840.00', + current: '3,920.50', + change: '+80.50', + changeDir: 'up', + status: 'Active' + }, + { + sl: '08', + name: 'Interest Accrued on Time Deposits', + sub: 'Accrued but unpaid interest on FD/RD', + code: '3008-IAT', + opening: '842.60', + current: '918.40', + change: '+75.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '09', + name: 'Long-term Deposits', + sub: 'Tenor >5 years · 7.5%', + code: '3009-LTD', + opening: '5,620.40', + current: '5,818.60', + change: '+198.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '10', + name: 'Tax Saving Deposits', + sub: 'Sec 80C FDs — 5-year lock-in · 6.8%', + code: '3010-TSD', + opening: '1,240.80', + current: '1,318.40', + change: '+77.60', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '11', + name: 'Foreign Currency Deposits', + sub: 'FCNR(B), RFC — USD/EUR/GBP', + code: '3011-FCD', + opening: '2,184.60', + current: '2,348.20', + change: '+163.60', + changeDir: 'up', + status: 'Active' + }, + { + sl: '12', + name: 'Time Liabilities to Other Banks', + sub: 'Inter-bank term deposits — call money', + code: '3012-TLB', + opening: '1,840.20', + current: '1,924.40', + change: '+84.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '13', + name: 'Term Borrowings', + sub: 'Bonds, debentures, refinance · 8.1%', + code: '3013-TBR', + opening: '1,018.40', + current: '1,118.50', + change: '+100.10', + changeDir: 'up', + status: 'Active' + } ]; bankingAssets = [ - { sl: '01', name: 'Balances with Other Banks', sub: 'Nostro / Vostro balances — SBI, PNB, BOB', code: '4001-BWB', opening: '3,840.20', current: '4,112.40', change: '+272.20', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Money at Call with Banks', sub: 'Overnight & short-notice call money', code: '4002-MCB', opening: '2,180.50', current: '2,418.60', change: '+238.10', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Loans to Banks (Inter-Bank Lending)', sub: 'Term loans & credit lines to SCBs', code: '4003-LTB', opening: '1,580.00', current: '1,709.20', change: '+129.20', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Balances with Other Banks', + sub: 'Nostro / Vostro balances — SBI, PNB, BOB', + code: '4001-BWB', + opening: '3,840.20', + current: '4,112.40', + change: '+272.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Money at Call with Banks', + sub: 'Overnight & short-notice call money', + code: '4002-MCB', + opening: '2,180.50', + current: '2,418.60', + change: '+238.10', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Loans to Banks (Inter-Bank Lending)', + sub: 'Term loans & credit lines to SCBs', + code: '4003-LTB', + opening: '1,580.00', + current: '1,709.20', + change: '+129.20', + changeDir: 'up', + status: 'Active' + } ]; liquidAssets = [ - { sl: '01', name: 'Central Government Securities', sub: 'G-Secs held in HTM, AFS & HFT portfolios', code: '5001-CGS', opening: '6,840.50', current: '7,218.40', change: '+377.90', changeDir: 'up', status: 'Active', category: 'G-Sec' }, - { sl: '02', name: 'State Development Loans (SDLs)', sub: 'State govt bonds — various maturities', code: '5002-SDL', opening: '2,420.30', current: '2,584.60', change: '+164.30', changeDir: 'up', status: 'Active', category: 'SDL' }, - { sl: '03', name: 'Treasury Bills (91/182/364 day)', sub: 'Short-term govt securities — discounted', code: '5003-TB', opening: '1,840.20', current: '1,962.80', change: '+122.60', changeDir: 'up', status: 'Active', category: 'T-Bill' }, - { sl: '04', name: 'Gold (valued at current market)', sub: 'Gold reserves held per RBI valuation norms', code: '5004-GLD', opening: '680.40', current: '724.60', change: '+44.20', changeDir: 'up', status: 'Active', category: 'Gold' }, - { sl: '05', name: 'Special Deposit Scheme with RBI', sub: 'Deposits placed under SDS — SLR eligible', code: '5005-SDS', opening: '420.00', current: '420.00', change: '0.00', changeDir: 'up', status: 'Active', category: 'RBI Dep' }, - { sl: '06', name: 'NABARD / SIDBI / NHB Bonds', sub: 'PSE bonds approved as SLR-eligible', code: '5006-PSE', opening: '310.80', current: '328.40', change: '+17.60', changeDir: 'up', status: 'Active', category: 'PSE Bond' } + { + sl: '01', + name: 'Central Government Securities', + sub: 'G-Secs held in HTM, AFS & HFT portfolios', + code: '5001-CGS', + opening: '6,840.50', + current: '7,218.40', + change: '+377.90', + changeDir: 'up', + status: 'Active', + category: 'G-Sec' + }, + { + sl: '02', + name: 'State Development Loans (SDLs)', + sub: 'State govt bonds — various maturities', + code: '5002-SDL', + opening: '2,420.30', + current: '2,584.60', + change: '+164.30', + changeDir: 'up', + status: 'Active', + category: 'SDL' + }, + { + sl: '03', + name: 'Treasury Bills (91/182/364 day)', + sub: 'Short-term govt securities — discounted', + code: '5003-TB', + opening: '1,840.20', + current: '1,962.80', + change: '+122.60', + changeDir: 'up', + status: 'Active', + category: 'T-Bill' + }, + { + sl: '04', + name: 'Gold (valued at current market)', + sub: 'Gold reserves held per RBI valuation norms', + code: '5004-GLD', + opening: '680.40', + current: '724.60', + change: '+44.20', + changeDir: 'up', + status: 'Active', + category: 'Gold' + }, + { + sl: '05', + name: 'Special Deposit Scheme with RBI', + sub: 'Deposits placed under SDS — SLR eligible', + code: '5005-SDS', + opening: '420.00', + current: '420.00', + change: '0.00', + changeDir: 'up', + status: 'Active', + category: 'RBI Dep' + }, + { + sl: '06', + name: 'NABARD / SIDBI / NHB Bonds', + sub: 'PSE bonds approved as SLR-eligible', + code: '5006-PSE', + opening: '310.80', + current: '328.40', + change: '+17.60', + changeDir: 'up', + status: 'Active', + category: 'PSE Bond' + } ]; close(): void { diff --git a/src/app/home/dashboard/dashboard.component.html b/src/app/home/dashboard/dashboard.component.html index ded889e48..3d389bd65 100644 --- a/src/app/home/dashboard/dashboard.component.html +++ b/src/app/home/dashboard/dashboard.component.html @@ -201,20 +201,45 @@
- -
+ +
-
-
Portfolio Quality
+
+
Credit Information Companies
+
Data Quality Report (DQR) — as per RBI Guidelines
-
Decay Analysis
-
Moderate
+ +
+ TransUnion CIBIL + Credit Information Bureau India Limited +
+
98.2%
-
TSWM
-
Active
+ +
+ Experian + Experian Credit Information Company of India +
+
97.5%
+
+
+ +
+ Equifax + Equifax Credit Information Services Pvt. Ltd. +
+
94.8%
+
+
+ +
+ CRIF High Mark + CRIF High Mark Credit Information Services +
+
96.1%
@@ -305,6 +330,5 @@ RBI circular pending review — compliance action due by month end.
- diff --git a/src/app/home/dashboard/dashboard.component.scss b/src/app/home/dashboard/dashboard.component.scss index 85c44f4e7..79997ebac 100644 --- a/src/app/home/dashboard/dashboard.component.scss +++ b/src/app/home/dashboard/dashboard.component.scss @@ -45,6 +45,7 @@ $purple: #7c3aed; .search-row { width: 100%; margin: 0; + mat-card-content { width: 100%; } @@ -63,7 +64,7 @@ $purple: #7c3aed; display: flex; align-items: center; gap: 8px; - margin: 6px 0 6px; + margin: 6px 0; padding: 5px 12px; border-radius: 6px; font-size: 14px; @@ -78,18 +79,22 @@ $purple: #7c3aed; background: #e3f2fd; color: #0d47a1; } + &.financial { background: #e8f5e9; color: #1b5e20; } + &.deposits { background: #f3e5f5; color: #6a1b9a; } + &.risk { background: #fce4ec; color: #b71c1c; } + &.alerts { background: #fff3e0; color: #e65100; @@ -105,12 +110,15 @@ $purple: #7c3aed; .four-col { grid-template-columns: repeat(4, 1fr); } + .five-col { grid-template-columns: repeat(5, 1fr); } + .three-col { grid-template-columns: repeat(3, 1fr); } + .two-col { grid-template-columns: repeat(2, 1fr); } @@ -153,15 +161,19 @@ $purple: #7c3aed; &.blue { background: $blue; } + &.green { background: $green; } + &.orange { background: $orange; } + &.red { background: $red; } + &.purple { background: $purple; } @@ -194,10 +206,12 @@ $purple: #7c3aed; background: #ecfdf5; color: #065f46; } + &.warning { background: #fffbeb; color: #92400e; } + &.info { background: #eff6ff; color: #1e40af; @@ -268,6 +282,7 @@ $purple: #7c3aed; font-size: 12px; color: #94a3b8; } + .metric-value { font-size: 14px; font-weight: 700; @@ -339,6 +354,7 @@ $purple: #7c3aed; background: #ecfdf5; color: #065f46; } + &.exceed { background: #fef2f2; color: #991b1b; @@ -365,15 +381,19 @@ $purple: #7c3aed; &.blue { background: $blue; } + &.green { background: $green; } + &.purple { background: $purple; } + &.orange { background: $orange; } + &.red { background: $red; } @@ -441,9 +461,11 @@ $purple: #7c3aed; .compact-icon { font-size: 14px; + &.purple-text { color: $purple; } + &.orange-text { color: $orange; } @@ -469,13 +491,16 @@ $purple: #7c3aed; color: #64748b; margin-bottom: 2px; } + .da-value { font-size: 20px; font-weight: 800; margin-bottom: 6px; + &.deposit { color: $blue; } + &.advance { color: $purple; } @@ -497,6 +522,7 @@ $purple: #7c3aed; &.deposit-fill { background: linear-gradient(90deg, $blue, #42a5f5); } + &.advance-fill { background: linear-gradient(90deg, $purple, #a78bfa); } @@ -532,9 +558,11 @@ $purple: #7c3aed; .psl-bar { height: 100%; border-radius: 5px; + &.weaker { background: $orange; } + &.msme { background: $green; } @@ -548,38 +576,94 @@ $purple: #7c3aed; text-align: right; } -/* ─── Portfolio Quality ─── */ +/* ─── Credit Information Companies — DQR ─── */ +.cic-card { + .card-top { + margin-bottom: 0; + } +} + +.cic-subtitle { + font-size: 11px; + font-weight: 600; + color: #94a3b8; + letter-spacing: 0.3px; + margin: 2px 0 10px; +} + .quality-grid { display: flex; flex-direction: column; - gap: 14px; - margin-top: 8px; + gap: 0; + margin-top: 0; } .quality-item { display: flex; - justify-content: space-between; align-items: center; + gap: 10px; + padding: 10px 0; + border-bottom: 1px solid #f1f5f9; + + .quality-label { + flex: 1; + min-width: 0; + } + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } +} + +.cic-logo { + width: 34px; + height: 34px; + border-radius: 7px; + flex-shrink: 0; + object-fit: contain; } .quality-label { - font-size: 13px; - color: #475569; + display: flex; + flex-direction: column; + gap: 1px; } -.quality-badge { - font-size: 12px; - font-weight: 700; +.cic-name { + font-size: 13px; + font-weight: 600; + color: #1e293b; +} + +.cic-full { + font-size: 10px; + color: #94a3b8; + line-height: 1.3; +} + +.dqr-percent { + font-size: 14px; + font-weight: 800; padding: 4px 12px; border-radius: 20px; + flex-shrink: 0; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + letter-spacing: 0.3px; + + &.good { + background: #ecfdf5; + color: #065f46; + } &.moderate { background: #fffbeb; color: #92400e; } - &.active { - background: #ecfdf5; - color: #065f46; + + &.low { + background: #fef2f2; + color: #991b1b; } } @@ -629,10 +713,12 @@ $purple: #7c3aed; &.success { color: #065f46; } + &.warning { color: #b45309; } } + .npa-note { font-size: 11px; color: #94a3b8; @@ -664,9 +750,11 @@ $purple: #7c3aed; &.green { background: $green; } + &.yellow { background: $orange; } + &.red { background: $red; } @@ -715,9 +803,11 @@ $purple: #7c3aed; .four-col { grid-template-columns: repeat(2, 1fr); } + .five-col { grid-template-columns: repeat(3, 1fr); } + .three-col { grid-template-columns: repeat(2, 1fr); } @@ -733,6 +823,7 @@ $purple: #7c3aed; .card-big-value { font-size: 26px; } + .mini-value { font-size: 18px; } @@ -742,5 +833,4 @@ $purple: #7c3aed; gap: 4px; text-align: center; } - } diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 28d06aa3a..37a33e9a2 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -11,18 +11,22 @@
-

{{ 'labels.text.Welcome' | translate }}, {{ username }}

+

+ {{ 'labels.text.Welcome' | translate }}, {{ username }} +

{{ tenant | titlecase }} · CBSNexora Core Banking Platform

@@ -166,7 +182,11 @@ - + + + +
+ +
+ + @if (activeSection === 'alerts') { +
+
+ KYC Alert Dashboard +
+ + +
+
+ +
+
+
47
+
Expiring in 30 days
+
▲ 12 this month
+
+
+
23
+
Debit Frozen
+
Action required
+
+
+
8
+
Inoperative
+
Escalating
+
+
+
3
+
Dormant
+
RBI reportable
+
+
+
18
+
Notices Today
+
SMS + Email
+
+
+ +
+ + + + + +
+ +
+
+
+
Accounts Requiring Action
+ Showing {{ alertRows.length }} of 81 · Click row for client details +
+
{{ item.code }}{{ item.category }} + {{ item.category }} + {{ item.opening }} {{ item.current }}{{ item.change }}{{ item.status }} + {{ item.change }} + + {{ item.status }} +
+ + + + + + + + + + + + + + + @for (row of alertRows; track row.id) { + + + + + + + + + + + + } + +
CustomerRiskExpiry DateDays LeftNoticesStatePending ActionNext Stage
+
{{ row.name }}
+
{{ row.account }}
+
+ {{ row.riskLabel.split(' ')[0] }} + {{ row.expiry }} +
+ {{ row.daysLeft > 0 ? row.daysLeft : '—' }} +
+
+
+
+
+ {{ row.noticesSent }}/3 + + {{ stateLabel(row.state) }} + + {{ row.noticePending }} + + {{ row.nextStage }} + + +
+
+
+ } + + + @if (activeSection === 'notif') { +
+
+ Notification Scheduler +
+ + +
+
+ +
+
+
+
+
3-Notice Dispatch Timeline
+ Expiry: 30 Apr 2027 · Window: 31 Mar – 30 Apr +
+
+
+
+
+
1
+
+
+
+
Notice 1 — Alert window opens
+
31 March 2027 · Auto-scheduled · ✓ Delivered
+
+ SMS + Email +
+
+
+
+
+
2
+
+
+
+
Notice 2 — Follow-up
+
10 April 2027 · +10 days · ⏳ Pending
+
+ SMS + Email + IVR Call + Reg. Post +
+
+
+
+
+
3
+
+
+
+
Notice 3 — Final notice
+
20 April 2027 · +20 days · ✗ Not dispatched
+
+ SMS + Email + IVR + Reg. Post + Branch Visit +
+
+ + +
+
+
+
+
+
!
+
+
+
+
Debit Freeze — if no action
+
30 April 2027 · Auto-triggered on expiry
+
+ Auto-triggered + Credits allowed + Debits blocked +
+
+
+
+
+
!
+
+
+
+
Inoperative — +60 days
+
30 June 2027 · No compliance
+
All txns blocked
+
+
+
+
!
+
+
Dormant — +150 days
+
30 September 2027
+
+ Dormant + RBI reporting +
+
+
+
+
+
+ +
+
+
+
+
Channels — Notice 3
+
+
+
+
📱
+
SMS
+
Registered mobile
+
+
+
📧
+
Email
+
Registered email
+
+
+
📞
+
IVR Call
+
Outbound IVR
+
+
+
📮
+
Reg. Post
+
India Post
+
+
+
🏦
+
Branch Visit
+
Manual
+
+
+
📹
+
Video KYC
+
V-CIP app
+
+
+
+ +
+
+
+
India Post Notice
+
+
+
+
Dispatch Date
+
10 April 2027
+
+
+
Reg. Post Docket No.
+
RP-BPL-20270410-443
+
+
+
Status
+
In Transit
+
+ +
+
+ +
+
+
+
Branch Visit
+
+
+
Scheduled Date
+ +
Officer
+ + +
+
+
+
+
+ } + + + @if (activeSection === 'audit') { +
+
+ Audit Inventory +
+ + +
+
+ +
+ + + + + +
+ +
+
+
+
KYC Notice Log
+ Click row for client details +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Account / CustomerNoticeChannelDispatchedReference / DocketDeliveryClient ActionOfficer
001 +
Rajesh K. Sharma
+
ACC-BPL-2024-00847
+
Notice 1SMS + Email31 Mar 2027MSG-2027-0331-001✓ DeliveredAwaitedScheduler
002 +
Rajesh K. Sharma
+
ACC-BPL-2024-00847
+
Notice 2Reg. Post + IVR10 Apr 2027RP-BPL-20270410-443In TransitAwaitedScheduler
003 +
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 1SMS + Email10 Mar 2027MSG-2027-0310-003✓ DeliveredNo ResponseScheduler
004 +
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 2Reg. Post20 Mar 2027RP-BPL-20270320-091✓ DeliveredNo ResponseScheduler
005 +
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 3Branch Visit30 Mar 2027VIS-BPL-20270330-012✓ DoneRefusedA. Mishra
006 +
Suresh Patel
+
ACC-BPL-2019-00112
+
Notice 3Video KYC5 Feb 2027VKYC-2027-0205-007No ResponseNo ActionSystem
+
+ Showing 6 of 12 +
+ + +
+
+
+
+ } + + + @if (activeSection === 'status') { +
+
+ Account Status Management + +
+ +
+
+
Active
+
1,847
+
KYC valid
+
+
+
Alert Window
+
47
+
Expiring < 30d
+
+
+
Debit Freeze
+
23
+
Debits blocked
+
+
+
Inoperative
+
8
+
All txns blocked
+
+
+
Dormant
+
3
+
RBI reportable
+
+
+ +
+
+
+
+
Auto-Transition Rules
+
+
+
+
+ Active → Debit FreezeEnabled +
+
+ KYC expires + 3 notices sent + no response → debits blocked, credits allowed +
+
+ Window: 31 Mar – 30 Apr + 3/3 notices +
+
+
+
+ Debit Freeze → InoperativeEnabled +
+
+60 days from expiry, still unresolved → all transactions blocked
+
Apr 30 – Jun 30
+
+
+
+ Inoperative → DormantEnabled +
+
+150 days from expiry → dormant, RBI Dormant Account reporting triggered
+
+ Jun 30 – Sep 30 + RBI notification +
+
+
+
+ +
+
+
+
Manual Override
+ Maker-Checker +
+
+
Account
+ +
Current State
+
Debit Freeze
+
Override Action
+ +
Reason
+ +
+ + +
+
+
⚠ Maker-Checker Required
+
+ All manual changes require second officer approval and are logged in audit inventory. +
+
+
+
+
+ +
+
+
+
Recent State Transitions
+ Click row for client details +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AccountFromToTriggerDateByAction
+
Priya Malhotra
+
ACC-BPL-2021-00231
+
ActiveDebit FreezeAuto — KYC Expired10 Apr 2027Scheduler + +
+
Suresh Patel
+
ACC-BPL-2019-00112
+
Debit FreezeInoperativeAuto — 60d elapsed15 May 2027Scheduler + +
+
Kavitha Nair
+
ACC-BPL-2018-00088
+
InoperativeDormantAuto — 150d elapsed1 Jun 2027Scheduler + +
+
+
+ } +
+ + + @if (modalOpen && current) { +
+
+
+
{{ current.av }}
+
+
{{ current.name }}
+
{{ current.meta }}
+
+ {{ stateLabel(current.state) }} + {{ current.riskLabel }} +
✕
+
+
+
+ + + + + +
+ + + @if (modalTab === 'overview') { +
Compliance Journey
+
+ @for (step of getStepperState(current); track step.label) { +
+
{{ stepIcon(step.state) }}
+
{{ step.label }}
+
+ } +
+ +
+
⚡
+
+
{{ stateLabel(current.state) }} — {{ current.daysLeft }} days to expiry
+
+ Next pending: {{ current.noticePending }}. Next stage: {{ current.nextStage }}. +
+
+
+ +
+
+
{{ current.noticesSent }}
+
Notices Sent
+
+
+
{{ noticesPending(current) }}
+
Notices Pending
+
+
+
{{ current.actionTaken.length }}
+
Actions Taken
+
+
+
{{ current.pendingActions.length }}
+
Pending Actions
+
+
+ +
+
+
KYC Information
+
+
+
PAN
+
{{ current.kyc.pan }}
+
+
+
PAN Status
+
+ {{ current.kyc.panStatus }} +
+
+
+
Aadhaar
+
{{ current.kyc.aadhaar }}
+
+
+
Aadhaar Status
+
+ {{ current.kyc.aadhaarStatus }} +
+
+
+
KYC Validity
+
{{ current.kyc.validity }}
+
+
+
Expiry Date
+
{{ current.expiry }}
+
+
+
+
+
Contact Details
+
+
+
Mobile
+
{{ current.contact.mobile }}
+
+
+
Email
+
{{ current.contact.email }}
+
+
+
Address
+
{{ current.contact.address }}
+
+
+
Branch
+
{{ current.contact.branch }}
+
+
+
+
+ } + + + @if (modalTab === 'timeline') { +
Notice & Escalation Timeline
+
+ @for (item of current.actionTaken; track $index) { +
+
✓
+
+
{{ item }}
+
Completed
+
+
+ } + @for (item of current.pendingActions; track $index) { +
+
!
+
+
{{ item }}
+
Pending action
+
+
+ } +
+ } + + + @if (modalTab === 'notices') { +
All Notices Dispatched
+ + + + + + + + + + + + + + @for (n of current.notices; track n.ref; let i = $index) { + + + + + + + + + + } + @if (noticesPending(current) > 0) { + + + + } + +
#NoticeChannelDateReferenceDeliveryResponse
{{ i + 1 }} + {{ + n.no + }} + {{ n.channel }}{{ n.date }} + {{ n.ref }} + + {{ n.delivery }} + + {{ n.response }} +
+ ⏳ {{ noticesPending(current) }} notice(s) still pending dispatch +
+ } + + + @if (modalTab === 'evidence') { + @if (!current.evidence) { +
+
📄
+
No evidence collected yet for this client.
+
Evidence is captured during branch visits or video KYC sessions.
+
+ } @else { +
+ {{ current.evidence.type === 'visit' ? 'Branch Visit Evidence' : 'Video KYC Session Log' }} +
+
+
+
Date
+
{{ current.evidence.date }}
+
+
+
Officer / Agent
+
{{ current.evidence.officer }}
+
+ @if (current.evidence.type === 'visit') { +
+
GPS Coordinates
+
{{ current.evidence.gps }}
+
+
+
Location
+
{{ current.evidence.location }}
+
+ } +
+
Reference / Docket
+
{{ current.evidence.docket || current.evidence.ref }}
+
+
+
Outcome
+
No Compliance
+
+
+ @if (current.evidence.type === 'visit') { +
Map Placeholder
+
+ {{ current.evidence.gps }} +
{{ current.evidence.location }}
+
+
Photo Evidence
+
Visit photo uploaded · Tap to view
+ } +
Officer Notes
+
{{ current.evidence.outcome }}
+
+ + +
+ } + } + + + @if (modalTab === 'action') { +
Available Actions
+
+ @for (action of current.pendingActions; track action) { +
+
+
{{ action }}
+
Initiate via available channels
+
+ +
+ } +
+ } +
+
+
+ } + + +
{{ toastMsg }}
+
diff --git a/src/app/kyc-management/kyc-management.component.scss b/src/app/kyc-management/kyc-management.component.scss new file mode 100644 index 000000000..f13fecb2f --- /dev/null +++ b/src/app/kyc-management/kyc-management.component.scss @@ -0,0 +1,1499 @@ +/** + * 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/. + */ + +:host { + --km-navy: #0b1e3d; + --km-navy-mid: #163260; + --km-teal: #0e7e6b; + --km-teal-l: #e0f4f0; + --km-teal-m: #1aaf96; + --km-amber: #d97706; + --km-amber-l: #fef3c7; + --km-red: #c0392b; + --km-red-l: #fdecea; + --km-orange: #ea580c; + --km-orange-l: #fff0e6; + --km-green: #15803d; + --km-green-l: #dcfce7; + --km-blue: #1d4ed8; + --km-blue-l: #eff6ff; + --km-g50: #f8fafc; + --km-g100: #f1f5f9; + --km-g200: #e2e8f0; + --km-g300: #cbd5e1; + --km-g400: #94a3b8; + --km-g500: #64748b; + --km-g600: #475569; + --km-g700: #334155; + --km-g800: #1e293b; + --km-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + background: var(--km-g50); + color: var(--km-g800); + font-size: 14px; + line-height: 1.5; +} + +.km-root { + position: relative; +} + +/* Inner tabs */ +.km-inner-nav { + background: var(--km-g100); + border-bottom: 1px solid var(--km-g200); + padding: 0 24px; + display: flex; + gap: 0; + overflow-x: auto; +} + +.km-itab { + padding: 11px 16px; + font-size: 12px; + font-weight: 500; + color: var(--km-g500); + border: none; + background: none; + cursor: pointer; + border-bottom: 2px solid transparent; + white-space: nowrap; + display: flex; + align-items: center; + gap: 6px; + transition: all 0.15s; + font-family: inherit; + + &:hover { + color: var(--km-g800); + } + + &.active { + color: var(--km-teal); + border-bottom-color: var(--km-teal); + font-weight: 600; + background: #fff; + } +} + +.km-cnt { + background: var(--km-teal-l); + color: var(--km-teal); + border-radius: 10px; + padding: 1px 7px; + font-size: 10px; + font-weight: 700; + + &--red { + background: var(--km-red-l); + color: var(--km-red); + } + + &--amb { + background: var(--km-amber-l); + color: var(--km-amber); + } +} + +.km-main { + padding: 20px 24px; + max-width: 1400px; +} + +.km-page-head { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.km-page-title { + font-size: 16px; + font-weight: 700; + color: var(--km-g900); +} + +.km-actions { + display: flex; + gap: 8px; +} + +/* Cards */ +.km-card { + background: #fff; + border: 1px solid var(--km-g200); + border-radius: 12px; + box-shadow: 0 1px 3px rgb(0 0 0 / 8%); +} + +.km-ch { + padding: 13px 18px; + border-bottom: 1px solid var(--km-g100); + display: flex; + align-items: center; + gap: 10px; +} + +.km-ct { + font-size: 13px; + font-weight: 600; + color: var(--km-g800); +} + +.km-ch-hint { + margin-left: auto; + font-size: 12px; + color: var(--km-g500); +} + +.km-cb { + padding: 18px; +} + +.km-cb-flush { + padding: 0; +} + +.km-ci { + width: 28px; + height: 28px; + border-radius: 7px; + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; + flex-shrink: 0; +} + +.km-ci-t { + background: var(--km-teal-l); + color: var(--km-teal); +} + +.km-ci-a { + background: var(--km-amber-l); + color: var(--km-amber); +} + +.km-ci-r { + background: var(--km-red-l); + color: var(--km-red); +} + +.km-ci-b { + background: var(--km-blue-l); + color: var(--km-blue); +} + +.km-ci-g { + background: var(--km-green-l); + color: var(--km-green); +} + +/* Badges */ +.km-b { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 9px; + border-radius: 20px; + font-size: 11px; + font-weight: 500; +} + +.km-b-gr { + background: var(--km-green-l); + color: var(--km-green); +} + +.km-b-am { + background: var(--km-amber-l); + color: var(--km-amber); +} + +.km-b-re { + background: var(--km-red-l); + color: var(--km-red); +} + +.km-b-or { + background: var(--km-orange-l); + color: var(--km-orange); +} + +.km-b-bl { + background: var(--km-blue-l); + color: var(--km-blue); +} + +.km-b-te { + background: var(--km-teal-l); + color: var(--km-teal); +} + +.km-b-gy { + background: var(--km-g100); + color: var(--km-g600); +} + +.km-b-nv { + background: #e8edf5; + color: var(--km-navy); +} + +.km-ml-auto { + margin-left: auto; +} + +.km-ml-12 { + margin-left: 12px; +} + +/* Buttons */ +.km-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + border-radius: 8px; + font-size: 12px; + font-weight: 500; + cursor: pointer; + border: 1px solid; + font-family: inherit; + transition: all 0.15s; +} + +.km-btn-t { + background: var(--km-teal); + border-color: var(--km-teal); + color: #fff; + + &:hover { + opacity: 0.9; + } +} + +.km-btn-ot { + background: #fff; + border-color: var(--km-teal); + color: var(--km-teal); + + &:hover { + background: var(--km-teal-l); + } +} + +.km-btn-n { + background: var(--km-navy); + border-color: var(--km-navy); + color: #fff; +} + +.km-btn-a { + background: var(--km-amber); + border-color: var(--km-amber); + color: #fff; +} + +.km-btn-r { + background: var(--km-red); + border-color: var(--km-red); + color: #fff; +} + +.km-btn-g { + background: #fff; + border-color: var(--km-g300); + color: var(--km-g600); + + &:hover { + background: var(--km-g50); + } +} + +.km-w { + width: 100%; +} + +/* Stat row */ +.km-stat-row { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; + margin-bottom: 16px; +} + +.km-stat { + background: #fff; + border: 1px solid var(--km-g200); + border-radius: 12px; + padding: 14px; + box-shadow: 0 1px 3px rgb(0 0 0 / 8%); + cursor: pointer; + transition: all 0.15s; + + &:hover { + box-shadow: 0 4px 12px rgb(0 0 0 / 10%); + } +} + +.km-stat-n { + font-size: 26px; + font-weight: 700; + line-height: 1; + margin-bottom: 3px; +} + +.km-stat-l { + font-size: 12px; + color: var(--km-g500); +} + +.km-stat-t { + font-size: 11px; + margin-top: 5px; +} + +/* Filters */ +.km-filters { + display: flex; + gap: 10px; + margin-bottom: 14px; + flex-wrap: wrap; +} + +.km-fsel, +.km-finp { + padding: 7px 12px; + border: 1px solid var(--km-g200); + border-radius: 8px; + font-size: 12px; + color: var(--km-g700); + background: #fff; + font-family: inherit; + + &:focus { + outline: none; + border-color: var(--km-teal); + } +} + +.km-finp { + flex: 1; + min-width: 180px; +} + +.km-ta { + resize: none; +} + +/* Tables */ +.km-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 9px 12px; + font-size: 11px; + font-weight: 600; + color: var(--km-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--km-g50); + border-bottom: 1px solid var(--km-g200); + } + + td { + padding: 10px 12px; + border-bottom: 1px solid var(--km-g100); + font-size: 13px; + vertical-align: middle; + } + + tr:last-child td { + border-bottom: none; + } + + tbody tr:hover td { + background: var(--km-g50); + cursor: pointer; + } +} + +.km-an { + font-weight: 500; + color: var(--km-g900); +} + +.km-ai { + font-size: 11px; + color: var(--km-g400); + font-family: 'DM Mono', monospace; +} + +.km-num { + color: var(--km-g400); +} + +.km-small { + font-size: 11px; +} + +.km-mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.km-red { + color: var(--km-red); +} + +/* State pills */ +.km-sp { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 9px; + border-radius: 20px; + font-size: 11px; + font-weight: 600; +} + +.km-sp-ac { + background: var(--km-green-l); + color: var(--km-green); +} + +.km-sp-fr { + background: var(--km-amber-l); + color: var(--km-amber); +} + +.km-sp-in { + background: var(--km-orange-l); + color: var(--km-orange); +} + +.km-sp-do { + background: var(--km-red-l); + color: var(--km-red); +} + +.km-sp-al { + background: #fef9c3; + color: #92400e; +} + +/* Progress bar */ +.km-pbar { + display: flex; + align-items: center; + gap: 8px; +} + +.km-pbar-bg { + flex: 1; + height: 5px; + background: var(--km-g200); + border-radius: 3px; + overflow: hidden; +} + +.km-pbar-fill { + height: 100%; + border-radius: 3px; +} + +.km-pbar-n { + font-size: 12px; + font-weight: 600; + color: var(--km-amber); + min-width: 28px; +} + +/* Reference cell */ +.km-ref { + font-family: 'DM Mono', monospace; + font-size: 11px; + background: var(--km-g100); + padding: 2px 6px; + border-radius: 4px; + color: var(--km-g600); +} + +/* Delivery badges */ +.km-del { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + border-radius: 20px; + font-size: 10px; + font-weight: 600; +} + +.km-d-ok { + background: var(--km-green-l); + color: var(--km-green); +} + +.km-d-pe { + background: var(--km-amber-l); + color: var(--km-amber); +} + +.km-d-fa { + background: var(--km-red-l); + color: var(--km-red); +} + +.km-d-tr { + background: var(--km-blue-l); + color: var(--km-blue); +} + +/* Pagination */ +.km-pagination { + padding: 11px 14px; + border-top: 1px solid var(--km-g100); + display: flex; + justify-content: space-between; + align-items: center; +} + +/* Notification scheduler grid */ +.km-notif-grid { + display: grid; + grid-template-columns: 1fr 310px; + gap: 14px; +} + +.km-side { + display: flex; + flex-direction: column; + gap: 12px; +} + +/* Notice timeline */ +.km-ntl { + display: flex; + flex-direction: column; +} + +.km-nt { + display: grid; + grid-template-columns: 36px 1fr; + gap: 12px; + padding: 13px 0; + border-bottom: 1px solid var(--km-g100); +} + +.km-nt-last { + border-bottom: none; + padding-bottom: 0; +} + +.km-nd-col { + display: flex; + flex-direction: column; + align-items: center; +} + +.km-nd { + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 700; + border: 2px solid; + flex-shrink: 0; +} + +.km-nd1 { + background: var(--km-blue-l); + border-color: #93c5fd; + color: var(--km-blue); +} + +.km-nd2 { + background: var(--km-amber-l); + border-color: #fcd34d; + color: var(--km-amber); +} + +.km-nd3 { + background: var(--km-orange-l); + border-color: #fdba74; + color: var(--km-orange); +} + +.km-ndf { + background: var(--km-red-l); + border-color: #fca5a5; + color: var(--km-red); +} + +.km-ndi { + background: #fee2e2; + border-color: #f87171; + color: #991b1b; +} + +.km-ndd { + background: var(--km-g100); + border-color: var(--km-g300); + color: var(--km-g500); +} + +.km-nl { + width: 1px; + flex: 1; + background: var(--km-g200); + margin-top: 4px; +} + +.km-nh { + font-size: 13px; + font-weight: 600; + color: var(--km-g800); +} + +.km-warn { + color: var(--km-amber); +} + +.km-warn-o { + color: var(--km-orange); +} + +.km-warn-d { + color: var(--km-g600); +} + +.km-ns { + font-size: 11px; + color: var(--km-g500); + margin-top: 1px; + margin-bottom: 5px; +} + +.km-ok { + color: var(--km-green); + font-weight: 600; +} + +.km-pending { + color: var(--km-amber); + font-weight: 600; +} + +.km-fail { + color: var(--km-red); + font-weight: 600; +} + +.km-ntags { + display: flex; + gap: 5px; + flex-wrap: wrap; +} + +.km-cp { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + border-radius: 20px; + font-size: 11px; + font-weight: 500; + border: 1px solid; +} + +.km-cp-sms { + background: #eff6ff; + color: #1d4ed8; + border-color: #93c5fd; +} + +.km-cp-mail { + background: #f0fdf4; + color: #15803d; + border-color: #86efac; +} + +.km-cp-ivr { + background: #faf5ff; + color: #7c3aed; + border-color: #c4b5fd; +} + +.km-cp-post { + background: #fff7ed; + color: #c2410c; + border-color: #fdba74; +} + +.km-cp-vis { + background: var(--km-teal-l); + color: var(--km-teal); + border-color: #5eead4; +} + +.km-cp-vid { + background: #ede9fe; + color: #5b21b6; + border-color: #a78bfa; +} + +.km-row-actions { + margin-top: 8px; + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +/* Channels grid */ +.km-channels { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.km-channel { + border: 1px solid var(--km-g200); + border-radius: 8px; + padding: 10px; + cursor: pointer; +} + +.km-channel-on { + border-color: var(--km-teal); + background: var(--km-teal-l); +} + +.km-channel-i { + font-size: 16px; + margin-bottom: 3px; +} + +.km-channel-n { + font-size: 11px; + font-weight: 600; +} + +.km-channel-d { + font-size: 10px; + color: var(--km-g500); +} + +/* Meta items */ +.km-mi { + background: var(--km-g50); + border: 1px solid var(--km-g100); + border-radius: 8px; + padding: 10px 12px; + margin-bottom: 8px; +} + +.km-ml { + font-size: 10px; + font-weight: 600; + color: var(--km-g400); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 3px; +} + +.km-mv { + font-size: 13px; + font-weight: 600; + color: var(--km-g800); +} + +/* Account status cards */ +.km-sc-row { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; + margin-bottom: 16px; +} + +.km-sc { + border: 1.5px solid; + border-radius: 12px; + padding: 14px; + cursor: pointer; + transition: all 0.15s; + + &:hover { + box-shadow: 0 4px 12px rgb(0 0 0 / 10%); + } +} + +.km-sc-ac { + border-color: #86efac; + background: var(--km-green-l); + + .km-sc-l, + .km-sc-d { + color: #166534; + } + + .km-sc-n { + color: var(--km-green); + } +} + +.km-sc-fr { + border-color: #fcd34d; + background: var(--km-amber-l); + + .km-sc-l, + .km-sc-d { + color: #92400e; + } + + .km-sc-n { + color: var(--km-amber); + } +} + +.km-sc-in { + border-color: #fdba74; + background: var(--km-orange-l); + + .km-sc-l, + .km-sc-d { + color: #7c2d12; + } + + .km-sc-n { + color: var(--km-orange); + } +} + +.km-sc-do { + border-color: #fca5a5; + background: var(--km-red-l); + + .km-sc-l, + .km-sc-d { + color: #7f1d1d; + } + + .km-sc-n { + color: var(--km-red); + } +} + +.km-sc-al { + border-color: #fde047; + background: #fef9c3; + + .km-sc-l, + .km-sc-d { + color: #713f12; + } + + .km-sc-n { + color: #92400e; + } +} + +.km-sc-l { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 3px; +} + +.km-sc-n { + font-size: 26px; + font-weight: 700; + margin-bottom: 2px; +} + +.km-sc-d { + font-size: 11px; +} + +/* 2-col layout */ +.km-r2 { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; +} + +.km-mt { + margin-top: 14px; +} + +.km-mb { + margin-bottom: 14px; +} + +/* Rules */ +.km-rule { + padding: 11px 16px; + border-bottom: 1px solid var(--km-g100); +} + +.km-rule-last { + border-bottom: none; +} + +.km-rule-h { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 4px; +} + +.km-rule-t { + font-weight: 600; + font-size: 13px; +} + +.km-rule-d { + font-size: 12px; + color: var(--km-g500); +} + +.km-rule-tags { + margin-top: 5px; + display: flex; + gap: 5px; + flex-wrap: wrap; +} + +/* Manual override warning */ +.km-warn-box { + margin-top: 10px; + padding: 10px; + background: var(--km-amber-l); + border-radius: 8px; + border: 1px solid #fcd34d; + font-size: 11px; +} + +.km-warn-t { + font-weight: 700; + color: var(--km-amber); +} + +/* MODAL */ +.km-overlay { + position: fixed; + inset: 0; + background: rgb(0 0 0 / 45%); + z-index: 1200; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.km-modal { + background: #fff; + border-radius: 16px; + width: 900px; + max-width: 100%; + max-height: 90vh; + display: flex; + flex-direction: column; + box-shadow: 0 20px 60px rgb(0 0 0 / 20%); +} + +.km-mh { + padding: 18px 22px; + border-bottom: 1px solid var(--km-g200); + display: flex; + align-items: center; + gap: 12px; + flex-shrink: 0; +} + +.km-mh-av { + width: 42px; + height: 42px; + border-radius: 50%; + background: linear-gradient(135deg, var(--km-teal), var(--km-navy-mid)); + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + font-weight: 700; + color: #fff; + flex-shrink: 0; +} + +.km-mh-name { + font-size: 16px; + font-weight: 700; + color: var(--km-g900); +} + +.km-mh-meta { + font-size: 12px; + color: var(--km-g500); + margin-top: 1px; +} + +.km-mclose { + width: 30px; + height: 30px; + border: 1px solid var(--km-g200); + border-radius: 7px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 15px; + color: var(--km-g500); + margin-left: auto; + flex-shrink: 0; + + &:hover { + background: var(--km-g100); + } +} + +.km-mbody { + overflow-y: auto; + flex: 1; + padding: 20px 22px; +} + +.km-mtab-bar { + display: flex; + gap: 2px; + background: var(--km-g100); + border-radius: 8px; + padding: 3px; + margin-bottom: 18px; +} + +.km-mtab { + flex: 1; + padding: 7px 8px; + font-size: 12px; + font-weight: 500; + text-align: center; + border-radius: 6px; + cursor: pointer; + color: var(--km-g500); + border: none; + background: transparent; + font-family: inherit; + + &.active { + background: #fff; + color: var(--km-g900); + border: 0.5px solid var(--km-g200); + font-weight: 600; + } +} + +.km-slbl { + font-size: 11px; + font-weight: 700; + color: var(--km-g400); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 10px; +} + +/* Stepper */ +.km-prog-steps { + display: flex; + align-items: flex-start; + gap: 0; + margin-bottom: 18px; +} + +.km-ps { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + position: relative; + + &:not(:last-child)::after { + content: ''; + position: absolute; + top: 14px; + left: 50%; + width: 100%; + height: 2px; + background: var(--km-g200); + z-index: 0; + } +} + +.km-ps-done::after { + background: var(--km-teal); +} + +.km-ps-dot { + width: 28px; + height: 28px; + border-radius: 50%; + background: #fff; + border: 2px solid var(--km-g300); + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 700; + color: var(--km-g400); + z-index: 1; + position: relative; +} + +.km-ps-done .km-ps-dot { + background: var(--km-teal); + border-color: var(--km-teal); + color: #fff; +} + +.km-ps-current .km-ps-dot { + background: #fff; + border-color: var(--km-amber); + color: var(--km-amber); +} + +.km-ps-danger .km-ps-dot { + background: #fff; + border-color: var(--km-red); + color: var(--km-red); +} + +.km-ps-label { + font-size: 10px; + font-weight: 500; + color: var(--km-g500); + text-align: center; + max-width: 70px; +} + +.km-ps-done .km-ps-label { + color: var(--km-teal); +} + +.km-ps-current .km-ps-label { + color: var(--km-amber); + font-weight: 600; +} + +.km-ps-danger .km-ps-label { + color: var(--km-red); + font-weight: 600; +} + +/* Next action banner */ +.km-next-banner { + border-radius: 8px; + padding: 12px 16px; + margin-bottom: 16px; + border: 1px solid; + display: flex; + align-items: center; + gap: 12px; +} + +.km-nb-alert { + background: var(--km-amber-l); + border-color: #fcd34d; +} + +.km-nb-urgent { + background: var(--km-orange-l); + border-color: #fdba74; +} + +.km-nb-freeze { + background: var(--km-amber-l); + border-color: #fcd34d; +} + +.km-nb-inop { + background: var(--km-orange-l); + border-color: #fdba74; +} + +.km-nb-dormant { + background: var(--km-red-l); + border-color: #fca5a5; +} + +.km-nb-icon { + font-size: 22px; + flex-shrink: 0; +} + +.km-nb-title { + font-size: 13px; + font-weight: 700; + margin-bottom: 2px; +} + +.km-nb-sub { + font-size: 12px; +} + +/* Modal stat strip */ +.km-mstat-row { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 10px; + margin-bottom: 16px; +} + +.km-mstat { + background: var(--km-g50); + border: 1px solid var(--km-g100); + border-radius: 8px; + padding: 12px; +} + +.km-mstat-n { + font-size: 20px; + font-weight: 700; + margin-bottom: 2px; + color: var(--km-teal); +} + +.km-mstat-l { + font-size: 11px; + color: var(--km-g500); +} + +/* Modal meta grid */ +.km-mg { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; +} + +.km-mi-span { + grid-column: span 2; +} + +/* Modal timeline */ +.km-mtl { + display: flex; + flex-direction: column; +} + +.km-mti { + display: grid; + grid-template-columns: 28px 1fr; + gap: 10px; + padding: 12px 0; + border-bottom: 1px solid var(--km-g100); + + &:last-child { + border-bottom: none; + } +} + +.km-mti-dot { + width: 24px; + height: 24px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + font-weight: 700; + border: 2px solid; + flex-shrink: 0; +} + +.km-mti-done { + background: var(--km-green-l); + border-color: var(--km-green); + color: var(--km-green); +} + +.km-mti-pend { + background: var(--km-amber-l); + border-color: var(--km-amber); + color: var(--km-amber); +} + +.km-mti-text { + font-size: 13px; + font-weight: 500; +} + +.km-done { + color: var(--km-g700); +} + +.km-pend { + color: var(--km-amber); +} + +.km-mti-sub { + font-size: 11px; + color: var(--km-g400); + margin-top: 2px; +} + +.km-pending-row { + text-align: center; + color: var(--km-amber); + font-size: 12px; + padding: 12px; + background: #fffbeb; +} + +/* Empty state */ +.km-empty { + text-align: center; + padding: 40px; + color: var(--km-g400); +} + +.km-empty-i { + font-size: 28px; + margin-bottom: 8px; +} + +.km-empty-s { + font-size: 12px; + margin-top: 4px; +} + +/* Evidence */ +.km-map { + background: var(--km-g100); + border: 1px solid var(--km-g200); + border-radius: 8px; + height: 110px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 6px; + font-size: 12px; + color: var(--km-g500); + margin-bottom: 14px; +} + +.km-photo { + background: var(--km-g100); + border: 1px solid var(--km-g200); + border-radius: 8px; + height: 80px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + color: var(--km-g500); + margin-bottom: 14px; +} + +.km-notes { + background: var(--km-g50); + border: 1px solid var(--km-g200); + border-radius: 8px; + padding: 12px; + font-size: 12px; + color: var(--km-g700); + line-height: 1.6; +} + +/* Action panel */ +.km-action-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.km-action-row { + background: var(--km-g50); + border: 1px solid var(--km-g200); + border-radius: 8px; + padding: 12px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.km-action-t { + font-size: 13px; + font-weight: 600; +} + +.km-action-d { + font-size: 12px; + color: var(--km-g500); +} + +/* Toast */ +.km-toast { + position: fixed; + bottom: 24px; + right: 24px; + background: var(--km-g900); + color: #fff; + padding: 10px 16px; + border-radius: 8px; + font-size: 13px; + z-index: 1300; + opacity: 0; + transform: translateY(8px); + transition: all 0.25s; + pointer-events: none; + + &.show { + opacity: 1; + transform: translateY(0); + } +} + +@media (width <= 900px) { + .km-stat-row, + .km-sc-row { + grid-template-columns: repeat(2, 1fr); + } + + .km-r2, + .km-notif-grid { + grid-template-columns: 1fr; + } + + .km-mstat-row { + grid-template-columns: repeat(2, 1fr); + } + + .km-prog-steps { + flex-wrap: wrap; + gap: 10px; + } + + .km-ps::after { + display: none; + } +} diff --git a/src/app/kyc-management/kyc-management.component.ts b/src/app/kyc-management/kyc-management.component.ts new file mode 100644 index 000000000..c5628277c --- /dev/null +++ b/src/app/kyc-management/kyc-management.component.ts @@ -0,0 +1,636 @@ +/** + * 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 } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +interface KmNotice { + no: string; + channel: string; + date: string; + ref: string; + delivery: string; + response: string; +} + +interface KmEvidence { + type: 'visit' | 'vkyc'; + date: string; + officer: string; + gps?: string; + location?: string; + ref?: string; + outcome: string; + docket?: string; +} + +interface KmClient { + id: string; + av: string; + name: string; + meta: string; + state: 'alert' | 'freeze' | 'inop' | 'dormant' | 'urgent'; + risk: 'low' | 'mid' | 'high'; + riskLabel: string; + expiry: string; + daysLeft: number; + account: string; + kyc: { + type: string; + initiated: string; + lastVerified: string; + validity: string; + pan: string; + aadhaar: string; + panStatus: string; + aadhaarStatus: string; + panLink: string; + }; + contact: { + mobile: string; + email: string; + address: string; + branch: string; + }; + notices: KmNotice[]; + noticesSent: number; + noticePending: string; + nextStage: string; + pendingActions: string[]; + actionTaken: string[]; + evidence: KmEvidence | null; +} + +@Component({ + selector: 'mifosx-kyc-management', + templateUrl: './kyc-management.component.html', + styleUrls: ['./kyc-management.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FormsModule, + FaIconComponent + ] +}) +export class KycManagementComponent implements OnInit { + activeSection: 'alerts' | 'notif' | 'audit' | 'status' = 'alerts'; + + modalOpen = false; + modalTab: 'overview' | 'timeline' | 'notices' | 'evidence' | 'action' = 'overview'; + current: KmClient | null = null; + + toastMsg = ''; + toastVisible = false; + private toastTimer: any; + + clients: Record = { + rajesh: { + id: 'rajesh', + av: 'RS', + name: 'Rajesh Kumar Sharma', + meta: 'ACC-BPL-2024-00847 · Savings A/c · Bhopal Main', + account: 'ACC-BPL-2024-00847', + state: 'alert', + risk: 'low', + riskLabel: 'Low Risk', + expiry: '30 Apr 2027', + daysLeft: 15, + kyc: { + type: 'Individual', + initiated: '4 Apr 2026', + lastVerified: '4 Apr 2026', + validity: '10 Years', + pan: 'AQRPV8345E', + aadhaar: 'XXXXXXXX6039', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-99041', + email: 'r****@gmail.com', + address: 'House No. 42, Sector 5, Bhopal MP 462001', + branch: 'Bhopal Main' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '31 Mar 2027', + ref: 'MSG-2027-0331-001', + delivery: 'Delivered', + response: 'No Action' + }, + { + no: 'N2', + channel: 'Reg. Post + IVR', + date: '10 Apr 2027', + ref: 'RP-BPL-20270410-443', + delivery: 'In Transit', + response: 'Pending' + } + ], + noticesSent: 2, + noticePending: 'Notice 3', + nextStage: 'Debit Freeze', + pendingActions: [ + 'Send Notice 3 (due 20 Apr 2027)', + 'Schedule Branch Visit', + 'Send Video KYC link' + ], + actionTaken: [ + 'Notice 1 delivered', + 'Notice 2 dispatched via Reg. Post' + ], + evidence: null + }, + priya: { + id: 'priya', + av: 'PM', + name: 'Priya Malhotra', + meta: 'ACC-BPL-2021-00231 · Savings A/c · Bhopal Main', + account: 'ACC-BPL-2021-00231', + state: 'freeze', + risk: 'mid', + riskLabel: 'Mid Risk', + expiry: '10 Apr 2027', + daysLeft: 0, + kyc: { + type: 'Individual', + initiated: '10 Apr 2019', + lastVerified: '10 Apr 2019', + validity: '8 Years', + pan: 'BKJPV1234A', + aadhaar: 'XXXXXXXX7721', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-55321', + email: 'p****@yahoo.com', + address: 'Flat 12B, Arera Colony, Bhopal MP 462016', + branch: 'Bhopal Main' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '10 Mar 2027', + ref: 'MSG-2027-0310-003', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N2', + channel: 'Reg. Post', + date: '20 Mar 2027', + ref: 'RP-BPL-20270320-091', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N3', + channel: 'Branch Visit', + date: '30 Mar 2027', + ref: 'VIS-BPL-20270330-012', + delivery: 'Visit Done', + response: 'Refused' + } + ], + noticesSent: 3, + noticePending: 'None — all 3 sent', + nextStage: 'Inoperative by 30 Jun 2027', + pendingActions: [ + 'Account in Debit Freeze', + 'Inoperative transition due 30 Jun 2027', + 'Maker-checker unfreeze pending' + ], + actionTaken: [ + 'Notice 1 delivered', + 'Notice 2 delivered via Reg. Post', + 'Notice 3 — Branch visit done, client refused', + 'Debit Freeze applied 10 Apr 2027' + ], + evidence: { + type: 'visit', + date: '30 Mar 2027', + officer: 'Anupam Mishra', + gps: '23.2599° N, 77.4126° E', + location: 'Arera Colony, Bhopal', + outcome: 'Client refused to sign self-declaration form. Informed about debit freeze implications.', + docket: 'VIS-BPL-20270330-012' + } + }, + suresh: { + id: 'suresh', + av: 'SP', + name: 'Suresh Patel', + meta: 'ACC-BPL-2019-00112 · Savings A/c · Bhopal South', + account: 'ACC-BPL-2019-00112', + state: 'inop', + risk: 'high', + riskLabel: 'High Risk', + expiry: '15 Mar 2027', + daysLeft: 0, + kyc: { + type: 'Individual', + initiated: '15 Mar 2025', + lastVerified: '15 Mar 2025', + validity: '2 Years', + pan: 'CXRTS5678B', + aadhaar: 'XXXXXXXX2211', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-88765', + email: 's****@gmail.com', + address: 'Village Ratibad, Bhopal MP 462044', + branch: 'Bhopal South' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '13 Feb 2027', + ref: 'MSG-2027-0213-006', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N2', + channel: 'Reg. Post + IVR', + date: '23 Feb 2027', + ref: 'RP-BPL-20270223-044', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N3', + channel: 'Video KYC', + date: '5 Mar 2027', + ref: 'VKYC-2027-0305-007', + delivery: 'No Response', + response: 'No Action' + } + ], + noticesSent: 3, + noticePending: 'None', + nextStage: 'Dormant by 30 Sep 2027', + pendingActions: [ + 'Account Inoperative — all txns blocked', + 'Dormant transition due 30 Sep 2027', + 'RBI Dormant Account reporting' + ], + actionTaken: [ + 'Notice 1 delivered', + 'Notice 2 delivered via Reg. Post', + 'Notice 3 — Video KYC invite sent (no response)', + 'Debit Freeze 15 Mar 2027', + 'Inoperative 15 May 2027' + ], + evidence: { + type: 'vkyc', + date: '5 Mar 2027', + officer: 'System', + ref: 'VKYC-2027-0305-007', + outcome: 'Video KYC session link sent. Client did not join within 48 hours. Session auto-expired.' + } + }, + kavitha: { + id: 'kavitha', + av: 'KN', + name: 'Kavitha Nair', + meta: 'ACC-BPL-2018-00088 · Savings A/c · Bhopal East', + account: 'ACC-BPL-2018-00088', + state: 'dormant', + risk: 'mid', + riskLabel: 'Mid Risk', + expiry: '1 Jan 2027', + daysLeft: 0, + kyc: { + type: 'Individual', + initiated: '1 Jan 2019', + lastVerified: '1 Jan 2019', + validity: '8 Years', + pan: 'DVJNK9012C', + aadhaar: 'XXXXXXXX4492', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-11092', + email: 'k****@rediff.com', + address: 'Plot 7, Awadhpuri, Bhopal MP 462010', + branch: 'Bhopal East' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '1 Dec 2026', + ref: 'MSG-2026-1201-009', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N2', + channel: 'Reg. Post', + date: '11 Dec 2026', + ref: 'RP-BPL-20261211-077', + delivery: 'Delivered', + response: 'No Response' + }, + { + no: 'N3', + channel: 'Branch Visit', + date: '22 Dec 2026', + ref: 'VIS-BPL-20261222-019', + delivery: 'Not Found', + response: 'Not Found at Address' + } + ], + noticesSent: 3, + noticePending: 'None', + nextStage: 'Dormant (Final) — RBI Reporting', + pendingActions: [ + 'RBI Dormant Account Report submission', + 'Branch Manager approval for any reversal', + 'Regional Manager sign-off required' + ], + actionTaken: [ + 'Notice 1–3 sent, all no response', + 'Debit Freeze 1 Jan 2027', + 'Inoperative 2 Mar 2027', + 'Dormant 1 Jun 2027', + 'RBI report auto-generated' + ], + evidence: { + type: 'visit', + date: '22 Dec 2026', + officer: 'Rekha Sharma', + gps: '23.2491° N, 77.4384° E', + location: 'Awadhpuri, Bhopal', + outcome: + 'Client not found at registered address. Neighbours confirmed relocation. Address verification required.', + docket: 'VIS-BPL-20261222-019' + } + }, + amit: { + id: 'amit', + av: 'AS', + name: 'Amit Singh', + meta: 'ACC-BPL-2024-01103 · Savings A/c · Bhopal Main', + account: 'ACC-BPL-2024-01103', + state: 'alert', + risk: 'low', + riskLabel: 'Low Risk', + expiry: '5 May 2027', + daysLeft: 20, + kyc: { + type: 'Individual', + initiated: '5 May 2016', + lastVerified: '5 May 2016', + validity: '10 Years', + pan: 'EKJPS4321D', + aadhaar: 'XXXXXXXX8834', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-22334', + email: 'a****@gmail.com', + address: 'C-22, Shivaji Nagar, Bhopal MP 462016', + branch: 'Bhopal Main' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '5 Apr 2027', + ref: 'MSG-2027-0405-011', + delivery: 'Delivered', + response: 'No Action' + } + ], + noticesSent: 1, + noticePending: 'Notice 2 & 3', + nextStage: 'Debit Freeze if no action', + pendingActions: [ + 'Send Notice 2 (due 15 Apr 2027)', + 'Send Notice 3 (due 25 Apr 2027)', + 'Branch visit if needed' + ], + actionTaken: ['Notice 1 delivered 5 Apr 2027'], + evidence: null + }, + meena: { + id: 'meena', + av: 'MJ', + name: 'Meena Joshi', + meta: 'ACC-BPL-2022-00567 · Savings A/c · Bhopal North', + account: 'ACC-BPL-2022-00567', + state: 'urgent', + risk: 'high', + riskLabel: 'High Risk', + expiry: '28 Apr 2027', + daysLeft: 3, + kyc: { + type: 'Individual', + initiated: '28 Apr 2025', + lastVerified: '28 Apr 2025', + validity: '2 Years', + pan: 'FJLMN6789E', + aadhaar: 'XXXXXXXX3309', + panStatus: 'Verified', + aadhaarStatus: 'Verified', + panLink: 'Linked' + }, + contact: { + mobile: '+91-XXXXX-77654', + email: 'm****@gmail.com', + address: 'Lane 4, Shyamla Hills, Bhopal MP 462002', + branch: 'Bhopal North' + }, + notices: [ + { + no: 'N1', + channel: 'SMS + Email', + date: '29 Mar 2027', + ref: 'MSG-2027-0329-014', + delivery: 'Delivered', + response: 'No Action' + }, + { + no: 'N2', + channel: 'Reg. Post + IVR', + date: '8 Apr 2027', + ref: 'RP-BPL-20270408-088', + delivery: 'Delivered', + response: 'No Response' + } + ], + noticesSent: 2, + noticePending: 'URGENT: Notice 3', + nextStage: 'Debit Freeze in 3 days', + pendingActions: [ + 'URGENT: Send Notice 3 NOW', + 'Schedule Branch Visit immediately', + 'Consider Video KYC as last resort' + ], + actionTaken: [ + 'Notice 1 delivered', + 'Notice 2 delivered via Reg. Post + IVR' + ], + evidence: null + } + }; + + alertRows: KmClient[] = []; + + ngOnInit(): void { + this.alertRows = Object.values(this.clients); + } + + showSection(section: 'alerts' | 'notif' | 'audit' | 'status'): void { + this.activeSection = section; + } + + openModal(id: string): void { + const c = this.clients[id]; + if (!c) return; + this.current = c; + this.modalTab = 'overview'; + this.modalOpen = true; + } + + closeModal(): void { + this.modalOpen = false; + this.current = null; + } + + showMTab(tab: 'overview' | 'timeline' | 'notices' | 'evidence' | 'action'): void { + this.modalTab = tab; + } + + toast(msg: string): void { + this.toastMsg = msg; + this.toastVisible = true; + if (this.toastTimer) clearTimeout(this.toastTimer); + this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); + } + + /** Compliance journey stepper state for a given client */ + getStepperState(c: KmClient): { label: string; state: 'done' | 'pending' | 'current' | 'danger' }[] { + return [ + { label: 'KYC Active', state: 'done' }, + { label: 'Alert Sent', state: c.noticesSent >= 1 ? 'done' : 'pending' }, + { label: 'Notice 2', state: c.noticesSent >= 2 ? 'done' : c.noticesSent >= 1 ? 'current' : 'pending' }, + { + label: 'Notice 3', + state: + c.noticesSent >= 3 ? 'done' : c.noticesSent >= 2 ? (c.state === 'urgent' ? 'danger' : 'current') : 'pending' + }, + { + label: 'Debit Freeze', + state: [ + 'freeze', + 'inop', + 'dormant' + ].includes(c.state) ? 'done' : c.noticesSent >= 3 ? 'danger' : 'pending' + }, + { label: 'Inoperative', state: [ + 'inop', + 'dormant' + ].includes(c.state) ? 'done' : 'pending' }, + { label: 'Dormant', state: c.state === 'dormant' ? 'danger' : 'pending' } + ]; + } + + stepIcon(state: string): string { + if (state === 'done') return '✓'; + if (state === 'danger') return '!'; + return '·'; + } + + stateLabel(state: string): string { + return ( + ( + { + alert: 'Alert Window', + freeze: 'Debit Freeze', + inop: 'Inoperative', + dormant: 'Dormant', + urgent: 'Urgent Alert' + } as Record + )[state] || state + ); + } + + stateClass(state: string): string { + return ( + ( + { + alert: 'sp-al', + freeze: 'sp-fr', + inop: 'sp-in', + dormant: 'sp-do', + urgent: 'sp-fr' + } as Record + )[state] || 'sp-al' + ); + } + + riskClass(risk: string): string { + return ({ low: 'b-gr', mid: 'b-am', high: 'b-re' } as Record)[risk] || 'b-gy'; + } + + /** Notices pending count (cap at 0) */ + noticesPending(c: KmClient): number { + return Math.max(0, 3 - c.noticesSent); + } + + deliveryClass(d: string): string { + const m: Record = { + Delivered: 'd-ok', + 'In Transit': 'd-tr', + 'No Response': 'd-fa', + Pending: 'd-pe', + 'Visit Done': 'd-ok', + 'Not Found': 'd-fa' + }; + return m[d] || 'd-pe'; + } + + barWidth(c: KmClient): number { + if (c.daysLeft <= 0) return 100; + return Math.max(2, Math.min(100, c.daysLeft * 3)); + } + + barColor(c: KmClient): string { + if (c.daysLeft <= 0) return 'var(--km-g400)'; + return c.daysLeft > 10 ? 'var(--km-amber)' : 'var(--km-red)'; + } + + responseClass(r: string): string { + const m: Record = { + 'No Action': 'd-pe', + 'No Response': 'd-fa', + Pending: 'd-pe', + Refused: 'd-fa', + 'Not Found at Address': 'd-fa', + Awaited: 'd-pe' + }; + return m[r] || 'd-pe'; + } +} diff --git a/src/app/kyc-management/kyc-management.module.ts b/src/app/kyc-management/kyc-management.module.ts new file mode 100644 index 000000000..77ced18c1 --- /dev/null +++ b/src/app/kyc-management/kyc-management.module.ts @@ -0,0 +1,22 @@ +/** + * 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 { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { KycManagementRoutingModule } from './kyc-management-routing.module'; +import { KycManagementComponent } from './kyc-management.component'; + +@NgModule({ + imports: [ + SharedModule, + KycManagementRoutingModule, + KycManagementComponent + ] +}) +export class KycManagementModule {} diff --git a/src/app/login/login-form/login-form.component.html b/src/app/login/login-form/login-form.component.html index c0a847ac6..6e4a1318d 100644 --- a/src/app/login/login-form/login-form.component.html +++ b/src/app/login/login-form/login-form.component.html @@ -10,7 +10,6 @@ @if (!oauthEnabled) {
-
@@ -32,7 +31,9 @@

{{ 'labels.heading.Users' | translate }}

-

{{ 'labels.text.Download offices template and Upload office excel files' | translate }}

+

+ {{ 'labels.text.Download offices template and Upload office excel files' | translate }} +

@@ -42,7 +43,9 @@

{{ 'labels.heading.Groups' | translate }}

-

{{ 'labels.text.Download groups template and Upload group excel files' | translate }}

+

+ {{ 'labels.text.Download groups template and Upload group excel files' | translate }} +

@@ -52,7 +55,9 @@

{{ 'labels.heading.Loan Accounts' | translate }}

-

{{ 'labels.text.Download loan accounts template and upload loan account excel files' | translate }}

+

+ {{ 'labels.text.Download loan accounts template and upload loan account excel files' | translate }} +

@@ -62,7 +67,9 @@

{{ 'labels.heading.Savings Accounts' | translate }}

-

{{ 'labels.text.Download savings accounts template and upload savings account excel files' | translate }}

+

+ {{ 'labels.text.Download savings accounts template and upload savings account excel files' | translate }} +

@@ -72,7 +79,12 @@

{{ 'labels.heading.Fixed Deposit Accounts' | translate }}

-

{{ 'labels.text.Download fixed deposit accounts template and upload fixed deposit account excel files' | translate }}

+

+ {{ + 'labels.text.Download fixed deposit accounts template and upload fixed deposit account excel files' + | translate + }} +

@@ -82,7 +94,9 @@

{{ 'labels.heading.Chart of Accounts' | translate }}

-

{{ 'labels.text.Download chart of accounts template and upload chart of account excel files' | translate }}

+

+ {{ 'labels.text.Download chart of accounts template and upload chart of account excel files' | translate }} +

@@ -92,7 +106,9 @@

{{ 'labels.heading.Share Accounts' | translate }}

-

{{ 'labels.text.Download share accounts template and upload share account excel files' | translate }}

+

+ {{ 'labels.text.Download share accounts template and upload share account excel files' | translate }} +

@@ -102,7 +118,9 @@

{{ 'labels.heading.Employees' | translate }}

-

{{ 'labels.text.Download loan accounts template and upload loan account excel files' | translate }}

+

+ {{ 'labels.text.Download loan accounts template and upload loan account excel files' | translate }} +

@@ -112,7 +130,9 @@

{{ 'labels.heading.Clients' | translate }}

-

{{ 'labels.text.Download clients template and upload clients excel files' | translate }}

+

+ {{ 'labels.text.Download clients template and upload clients excel files' | translate }} +

@@ -122,7 +142,9 @@

{{ 'labels.heading.Centers' | translate }}

-

{{ 'labels.text.Download centers template and upload centers excel files' | translate }}

+

+ {{ 'labels.text.Download centers template and upload centers excel files' | translate }} +

@@ -132,7 +154,9 @@

{{ 'labels.heading.Loan Repayments' | translate }}

-

{{ 'labels.text.Download loan repayments template and upload loan repayment excel files' | translate }}

+

+ {{ 'labels.text.Download loan repayments template and upload loan repayment excel files' | translate }} +

@@ -142,7 +166,11 @@

{{ 'labels.heading.Savings Transactions' | translate }}

-

{{ 'labels.text.Download savings transactions template and upload savings transaction excel files' | translate }}

+

+ {{ + 'labels.text.Download savings transactions template and upload savings transaction excel files' | translate + }} +

@@ -152,7 +180,12 @@

{{ 'labels.heading.Fixed Deposit Transactions' | translate }}

-

{{ 'labels.text.Download fixed deposit transactions template and upload fixed deposit transaction excel files' | translate }}

+

+ {{ + 'labels.text.Download fixed deposit transactions template and upload fixed deposit transaction excel files' + | translate + }} +

@@ -162,7 +195,12 @@

{{ 'labels.heading.Recurring Deposit Transactions' | translate }}

-

{{ 'labels.text.Download recurring deposit transactions template and upload recurring deposit transaction excel files' | translate }}

+

+ {{ + 'labels.text.Download recurring deposit transactions template and upload recurring deposit transaction excel files' + | translate + }} +

@@ -172,7 +210,9 @@

{{ 'labels.heading.Journal Entries' | translate }}

-

{{ 'labels.text.Download journal entries template and upload journal entries excel files' | translate }}

+

+ {{ 'labels.text.Download journal entries template and upload journal entries excel files' | translate }} +

@@ -182,10 +222,11 @@

{{ 'labels.heading.Guarantors' | translate }}

-

{{ 'labels.text.Download guarantors template and upload guarantor excel files' | translate }}

+

+ {{ 'labels.text.Download guarantors template and upload guarantor excel files' | translate }} +

-
diff --git a/src/app/organization/bulk-import/bulk-import.component.scss b/src/app/organization/bulk-import/bulk-import.component.scss index 11b969490..c2a8e640a 100644 --- a/src/app/organization/bulk-import/bulk-import.component.scss +++ b/src/app/organization/bulk-import/bulk-import.component.scss @@ -62,7 +62,7 @@ $gray-900: #0f172a; grid-template-columns: repeat(2, 1fr); gap: 12px; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: 1fr; } } @@ -83,7 +83,7 @@ $gray-900: #0f172a; &:hover { border-color: #cbd5e1; background: $gray-50; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + box-shadow: 0 2px 8px rgb(0 0 0 / 4%); .org-card__arrow { color: $gray-700; @@ -102,62 +102,62 @@ $gray-900: #0f172a; flex-shrink: 0; &--blue { - background: rgba(59, 130, 246, 0.1); + background: rgb(59 130 246 / 10%); color: #2563eb; } &--amber { - background: rgba(245, 158, 11, 0.1); + background: rgb(245 158 11 / 10%); color: #d97706; } &--green { - background: rgba(34, 197, 94, 0.1); + background: rgb(34 197 94 / 10%); color: #16a34a; } &--emerald { - background: rgba(16, 185, 129, 0.1); + background: rgb(16 185 129 / 10%); color: #059669; } &--purple { - background: rgba(139, 92, 246, 0.1); + background: rgb(139 92 246 / 10%); color: #7c3aed; } &--indigo { - background: rgba(99, 102, 241, 0.1); + background: rgb(99 102 241 / 10%); color: #4f46e5; } &--slate { - background: rgba(100, 116, 139, 0.1); + background: rgb(100 116 139 / 10%); color: #475569; } &--orange { - background: rgba(249, 115, 22, 0.1); + background: rgb(249 115 22 / 10%); color: #ea580c; } &--teal { - background: rgba(20, 184, 166, 0.1); + background: rgb(20 184 166 / 10%); color: #0d9488; } &--cyan { - background: rgba(6, 182, 212, 0.1); + background: rgb(6 182 212 / 10%); color: #0891b2; } &--rose { - background: rgba(244, 63, 94, 0.1); + background: rgb(244 63 94 / 10%); color: #e11d48; } &--red { - background: rgba(239, 68, 68, 0.1); + background: rgb(239 68 68 / 10%); color: #dc2626; } } @@ -196,8 +196,13 @@ $gray-900: #0f172a; // ── Dark mode ── :host-context(.dark-theme) { .org-header { - &__title { color: #e2e8f0; } - &__sub { color: #94a3b8; } + &__title { + color: #e2e8f0; + } + + &__sub { + color: #94a3b8; + } } .org-card { @@ -209,8 +214,16 @@ $gray-900: #0f172a; border-color: #444; } - &__title { color: #e2e8f0; } - &__desc { color: #64748b; } - &__arrow { color: #444; } + &__title { + color: #e2e8f0; + } + + &__desc { + color: #64748b; + } + + &__arrow { + color: #444; + } } } diff --git a/src/app/organization/bulk-loan-reassignmnet/bulk-loan-reassignmnet.component.scss b/src/app/organization/bulk-loan-reassignmnet/bulk-loan-reassignmnet.component.scss index 54c344613..fefb4811a 100644 --- a/src/app/organization/bulk-loan-reassignmnet/bulk-loan-reassignmnet.component.scss +++ b/src/app/organization/bulk-loan-reassignmnet/bulk-loan-reassignmnet.component.scss @@ -57,7 +57,9 @@ $gray-900: #202124; border: 1px solid $border; border-radius: 12px; padding: 0; - box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06), 0 1px 2px rgba(16, 24, 40, 0.04); + box-shadow: + 0 1px 3px rgb(16 24 40 / 6%), + 0 1px 2px rgb(16 24 40 / 4%); } .bl-section { @@ -89,7 +91,7 @@ $gray-900: #202124; grid-template-columns: 1fr 1fr; gap: 4px 20px; - @media (max-width: 600px) { + @media (width <= 600px) { grid-template-columns: 1fr; } } @@ -104,7 +106,7 @@ $gray-900: #202124; grid-template-columns: 1fr 1fr; gap: 20px; - @media (max-width: 600px) { + @media (width <= 600px) { grid-template-columns: 1fr; } } @@ -184,7 +186,7 @@ $gray-900: #202124; font-weight: 500; font-size: 13px; padding: 4px 20px !important; - box-shadow: 0 1px 2px rgba(19, 83, 160, 0.15); + box-shadow: 0 1px 2px rgb(19 83 160 / 15%); transition: all 0.2s ease; i { @@ -194,7 +196,7 @@ $gray-900: #202124; &:hover { background: #0f4688 !important; - box-shadow: 0 2px 6px rgba(19, 83, 160, 0.25); + box-shadow: 0 2px 6px rgb(19 83 160 / 25%); } &:disabled { diff --git a/src/app/organization/currencies/currencies.component.html b/src/app/organization/currencies/currencies.component.html index dd1cfc5eb..7ee445713 100644 --- a/src/app/organization/currencies/currencies.component.html +++ b/src/app/organization/currencies/currencies.component.html @@ -22,7 +22,13 @@
- diff --git a/src/app/organization/currencies/manage-currencies/manage-currencies.component.scss b/src/app/organization/currencies/manage-currencies/manage-currencies.component.scss index 8f6ad4669..975ac1b5b 100644 --- a/src/app/organization/currencies/manage-currencies/manage-currencies.component.scss +++ b/src/app/organization/currencies/manage-currencies/manage-currencies.component.scss @@ -15,34 +15,82 @@ $gray-500: #9aa0a6; $gray-700: #5f6368; $gray-900: #202124; -.mc-container { max-width: 700px; margin: 0 auto; padding: 20px 16px; } +.mc-container { + max-width: 700px; + margin: 0 auto; + padding: 20px 16px; +} .mc-header { - display: flex; align-items: center; gap: 14px; margin-bottom: 20px; - &__title { font-size: 18px; font-weight: 600; color: $gray-900; margin: 0; } + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 20px; + + &__title { + font-size: 18px; + font-weight: 600; + color: $gray-900; + margin: 0; + } } .mc-back { - border-radius: 8px !important; font-size: 12px !important; font-weight: 600 !important; - height: 32px !important; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } - &:hover { color: $primary !important; border-color: $primary !important; } + border-radius: 8px !important; + font-size: 12px !important; + font-weight: 600 !important; + height: 32px !important; + border-color: $border !important; + color: $gray-500 !important; + + i { + margin-right: 6px; + } + + &:hover { + color: $primary !important; + border-color: $primary !important; + } } -.mc-card { background: #fff; border: 1px solid $border; border-radius: 10px; overflow: hidden; } +.mc-card { + background: #fff; + border: 1px solid $border; + border-radius: 10px; + overflow: hidden; +} .mc-section { &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } + } + + & + & { + border-top: 1px solid $gray-200; } - & + & { border-top: 1px solid $gray-200; } } // Add currency form -.mc-add { padding: 16px 24px; } +.mc-add { + padding: 16px 24px; +} .mc-add__form { display: flex; @@ -50,7 +98,10 @@ $gray-900: #202124; gap: 12px; } -.mc-add__field { flex: 1; width: 100%; } +.mc-add__field { + flex: 1; + width: 100%; +} .mc-add__btn { flex-shrink: 0; @@ -61,11 +112,16 @@ $gray-900: #202124; margin-top: 4px; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } // Currency list -.mc-list { padding: 8px 24px 16px; } +.mc-list { + padding: 8px 24px 16px; +} .mc-currency { display: flex; @@ -73,12 +129,14 @@ $gray-900: #202124; justify-content: space-between; padding: 10px 14px; background: $gray-50; - border: 1px solid rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 4%); border-radius: 8px; margin-bottom: 6px; transition: background 0.12s; - &:hover { background: #f1f5f9; } + &:hover { + background: #f1f5f9; + } &__info { display: flex; @@ -110,7 +168,9 @@ $gray-900: #202124; line-height: 32px !important; font-size: 13px; - &:hover { background: rgba(239, 68, 68, 0.08) !important; } + &:hover { + background: rgb(239 68 68 / 8%) !important; + } } } @@ -121,6 +181,14 @@ $gray-900: #202124; padding: 32px 16px; color: #cbd5e1; - i { font-size: 28px; margin-bottom: 8px; } - p { font-size: 13px; color: $gray-500; margin: 0; } + i { + font-size: 28px; + margin-bottom: 8px; + } + + p { + font-size: 13px; + color: $gray-500; + margin: 0; + } } diff --git a/src/app/organization/employees/create-employee/create-employee.component.scss b/src/app/organization/employees/create-employee/create-employee.component.scss index 220221642..295219758 100644 --- a/src/app/organization/employees/create-employee/create-employee.component.scss +++ b/src/app/organization/employees/create-employee/create-employee.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ce-field { @@ -140,6 +147,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/employees/edit-employee/edit-employee.component.scss b/src/app/organization/employees/edit-employee/edit-employee.component.scss index e5620ee0b..1343831a5 100644 --- a/src/app/organization/employees/edit-employee/edit-employee.component.scss +++ b/src/app/organization/employees/edit-employee/edit-employee.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ee-field { @@ -140,6 +147,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/employees/employees.component.html b/src/app/organization/employees/employees.component.html index 75b407b8c..ac2e9ba66 100644 --- a/src/app/organization/employees/employees.component.html +++ b/src/app/organization/employees/employees.component.html @@ -24,13 +24,24 @@
-
- diff --git a/src/app/organization/employees/view-employee/view-employee.component.html b/src/app/organization/employees/view-employee/view-employee.component.html index 15517fc15..011377aa3 100644 --- a/src/app/organization/employees/view-employee/view-employee.component.html +++ b/src/app/organization/employees/view-employee/view-employee.component.html @@ -15,7 +15,13 @@

{{ employeeData.firstname }} {{ employeeData.lastname }}

- @@ -43,13 +49,9 @@
{{ 'labels.inputs.Is Loan Officer' | translate }}
@if (employeeData.isLoanOfficer) { - - Yes - + Yes } @else { - - No - + No }
diff --git a/src/app/organization/employees/view-employee/view-employee.component.scss b/src/app/organization/employees/view-employee/view-employee.component.scss index 4a0e33c4d..420e90c1d 100644 --- a/src/app/organization/employees/view-employee/view-employee.component.scss +++ b/src/app/organization/employees/view-employee/view-employee.component.scss @@ -60,9 +60,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -81,14 +86,16 @@ $gray-900: #202124; padding: 20px 28px 24px; background: #fff; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ve-cell { padding: 14px 16px; background: #f8fafc; border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 4%); margin: 3px; transition: background 0.12s; @@ -124,12 +131,12 @@ $gray-900: #202124; letter-spacing: 0.2px; &--active { - background: rgba(22, 163, 74, 0.1); + background: rgb(22 163 74 / 10%); color: #16a34a; } &--inactive { - background: rgba(220, 38, 38, 0.1); + background: rgb(220 38 38 / 10%); color: #dc2626; } } @@ -142,7 +149,9 @@ $gray-900: #202124; font-weight: 600; font-size: 13px; - i { font-size: 11px; } + i { + font-size: 11px; + } &--yes { color: #16a34a; diff --git a/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.html b/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.html index 4e99a391a..45705425d 100644 --- a/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.html +++ b/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.html @@ -121,7 +121,13 @@ -
diff --git a/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.scss b/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.scss index 889baceab..7b2e6ac60 100644 --- a/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.scss +++ b/src/app/organization/entity-data-table-checks/create-entity-data-table-checks/create-entity-data-table-checks.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -88,7 +93,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ce-field { @@ -120,6 +127,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/entity-data-table-checks/entity-data-table-checks.component.html b/src/app/organization/entity-data-table-checks/entity-data-table-checks.component.html index b3a40acf9..3ba2759a5 100644 --- a/src/app/organization/entity-data-table-checks/entity-data-table-checks.component.html +++ b/src/app/organization/entity-data-table-checks/entity-data-table-checks.component.html @@ -19,11 +19,21 @@
- +
- diff --git a/src/app/organization/fund-mapping/fund-mapping.component.html b/src/app/organization/fund-mapping/fund-mapping.component.html index e1dcf3893..c19da1d8b 100644 --- a/src/app/organization/fund-mapping/fund-mapping.component.html +++ b/src/app/organization/fund-mapping/fund-mapping.component.html @@ -136,10 +136,7 @@
- + {{ 'labels.inputs.Loan Outstanding Percentage' | translate }}
@@ -228,10 +225,7 @@
- + {{ 'labels.inputs.Loan Outstanding Amount' | translate }}
@@ -325,7 +319,13 @@ -
diff --git a/src/app/organization/fund-mapping/fund-mapping.component.scss b/src/app/organization/fund-mapping/fund-mapping.component.scss index 77e924257..4f3b36d75 100644 --- a/src/app/organization/fund-mapping/fund-mapping.component.scss +++ b/src/app/organization/fund-mapping/fund-mapping.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -96,10 +101,12 @@ $gray-900: #202124; grid-template-columns: repeat(3, 1fr); } - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: 1fr; - &--3col { grid-template-columns: 1fr; } + &--3col { + grid-template-columns: 1fr; + } } } @@ -127,7 +134,7 @@ $gray-900: #202124; padding: 0 0 8px; margin-top: -4px; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: 1fr; } } @@ -157,7 +164,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/holidays/create-holiday/create-holiday.component.scss b/src/app/organization/holidays/create-holiday/create-holiday.component.scss index fda995d3f..64a9c236d 100644 --- a/src/app/organization/holidays/create-holiday/create-holiday.component.scss +++ b/src/app/organization/holidays/create-holiday/create-holiday.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ch-field { @@ -143,6 +150,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/holidays/edit-holiday/edit-holiday.component.scss b/src/app/organization/holidays/edit-holiday/edit-holiday.component.scss index 02c33e574..d19852a5b 100644 --- a/src/app/organization/holidays/edit-holiday/edit-holiday.component.scss +++ b/src/app/organization/holidays/edit-holiday/edit-holiday.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .eh-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/holidays/holidays.component.html b/src/app/organization/holidays/holidays.component.html index f72ebd7d1..e74469ad5 100644 --- a/src/app/organization/holidays/holidays.component.html +++ b/src/app/organization/holidays/holidays.component.html @@ -22,7 +22,7 @@
- + {{ 'labels.inputs.Select Office' | translate }} @for (office of officeData; track office) { @@ -32,7 +32,13 @@
- diff --git a/src/app/organization/holidays/view-holidays/view-holidays.component.scss b/src/app/organization/holidays/view-holidays/view-holidays.component.scss index 1d31294af..e785a7498 100644 --- a/src/app/organization/holidays/view-holidays/view-holidays.component.scss +++ b/src/app/organization/holidays/view-holidays/view-holidays.component.scss @@ -52,9 +52,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vh-menu-btn { @@ -85,14 +90,16 @@ $gray-900: #202124; gap: 10px; padding: 20px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vh-cell { padding: 14px 16px; background: $gray-50; border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 4%); transition: background 0.12s; &:hover { @@ -130,7 +137,7 @@ $gray-900: #202124; } // Responsive -@media (max-width: 600px) { +@media (width <= 600px) { .vh-header { flex-direction: column; align-items: flex-start; diff --git a/src/app/organization/investors/investors.component.html b/src/app/organization/investors/investors.component.html index 2f3c003e5..5e3886a5a 100644 --- a/src/app/organization/investors/investors.component.html +++ b/src/app/organization/investors/investors.component.html @@ -9,9 +9,7 @@
- +

Investors

@@ -165,24 +163,40 @@

{{ 'labels.inputs.Details' | translate }}

- {{ 'labels.inputs.Principal Outstanding' | translate }} - {{ record.details.totalPrincipalOutstanding | formatNumber }} + {{ + 'labels.inputs.Principal Outstanding' | translate + }} + {{ + record.details.totalPrincipalOutstanding | formatNumber + }}
- {{ 'labels.inputs.Interest Outstanding' | translate }} - {{ record.details.totalInterestOutstanding | formatNumber }} + {{ + 'labels.inputs.Interest Outstanding' | translate + }} + {{ + record.details.totalInterestOutstanding | formatNumber + }}
{{ 'labels.inputs.Fees Outstanding' | translate }} - {{ record.details.totalFeeChargesOutstanding | formatNumber }} + {{ + record.details.totalFeeChargesOutstanding | formatNumber + }}
- {{ 'labels.inputs.Penalties Outstanding' | translate }} - {{ record.details.totalPenaltyChargesOutstanding | formatNumber }} + {{ + 'labels.inputs.Penalties Outstanding' | translate + }} + {{ + record.details.totalPenaltyChargesOutstanding | formatNumber + }}
{{ 'labels.inputs.Outstanding' | translate }} - {{ record.details.totalOutstanding | formatNumber }} + {{ + record.details.totalOutstanding | formatNumber + }}
{{ 'labels.inputs.Overpaid' | translate }} diff --git a/src/app/organization/investors/investors.component.scss b/src/app/organization/investors/investors.component.scss index 42092c64e..9f922c52f 100644 --- a/src/app/organization/investors/investors.component.scss +++ b/src/app/organization/investors/investors.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,8 +97,13 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 768px) { grid-template-columns: 1fr; } - @media (max-width: 1024px) and (min-width: 769px) { grid-template-columns: repeat(2, 1fr); } + @media (width <= 768px) { + grid-template-columns: 1fr; + } + + @media (width <= 1024px) and (width >= 769px) { + grid-template-columns: repeat(2, 1fr); + } } .inv-field { @@ -105,7 +115,9 @@ $gray-900: #202124; align-items: center; padding-top: 4px; - @media (max-width: 768px) { padding-top: 0; } + @media (width <= 768px) { + padding-top: 0; + } } .inv-search-btn { @@ -116,7 +128,10 @@ $gray-900: #202124; width: 100%; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } // Results area @@ -141,7 +156,9 @@ $gray-900: #202124; } @keyframes inv-spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } // Accordion & Panels @@ -210,7 +227,9 @@ $gray-900: #202124; gap: 16px; margin-bottom: 16px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } &__item { diff --git a/src/app/organization/loan-originators/create-loan-originator/create-loan-originator.component.scss b/src/app/organization/loan-originators/create-loan-originator/create-loan-originator.component.scss index 0a959d730..1466b9616 100644 --- a/src/app/organization/loan-originators/create-loan-originator/create-loan-originator.component.scss +++ b/src/app/organization/loan-originators/create-loan-originator/create-loan-originator.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .cl-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .cl-field { @@ -115,6 +122,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/loan-originators/edit-loan-originator/edit-loan-originator.component.scss b/src/app/organization/loan-originators/edit-loan-originator/edit-loan-originator.component.scss index 63f916f69..e6a4aafef 100644 --- a/src/app/organization/loan-originators/edit-loan-originator/edit-loan-originator.component.scss +++ b/src/app/organization/loan-originators/edit-loan-originator/edit-loan-originator.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .el-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .el-field { @@ -115,6 +122,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/loan-originators/loan-originators.component.html b/src/app/organization/loan-originators/loan-originators.component.html index ff6465678..6aed73ecb 100644 --- a/src/app/organization/loan-originators/loan-originators.component.html +++ b/src/app/organization/loan-originators/loan-originators.component.html @@ -19,11 +19,23 @@
- +
- diff --git a/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.html b/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.html index a908fd5aa..5e46b066a 100644 --- a/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.html +++ b/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.html @@ -13,7 +13,13 @@

{{ loanOriginatorData.name }}

-
diff --git a/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.scss b/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.scss index cbc811717..60b830a6e 100644 --- a/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.scss +++ b/src/app/organization/loan-originators/view-loan-originator/view-loan-originator.component.scss @@ -45,7 +45,10 @@ $gray-900: #202124; height: 32px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } @@ -57,9 +60,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vl-card { @@ -98,7 +106,9 @@ $gray-900: #202124; gap: 16px; padding: 20px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vl-detail { diff --git a/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.html b/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.html index ec8c97469..9e7c13167 100644 --- a/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.html +++ b/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.html @@ -92,7 +92,8 @@ {{ 'labels.inputs.Liability Account' | translate }} {{ - (definition.liabilityAccount | find: loanProvisioningCriteriaTemplate.glAccounts : 'id' : 'name') || '-' + (definition.liabilityAccount | find: loanProvisioningCriteriaTemplate.glAccounts : 'id' : 'name') || + '-' }} @@ -100,7 +101,10 @@ {{ 'labels.inputs.Expense Account' | translate }} - {{ (definition.expenseAccount | find: loanProvisioningCriteriaTemplate.glAccounts : 'id' : 'name') || '-' }} + {{ + (definition.expenseAccount | find: loanProvisioningCriteriaTemplate.glAccounts : 'id' : 'name') || + '-' + }} diff --git a/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.scss b/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.scss index f7a2673d8..f2de11f9e 100644 --- a/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.scss +++ b/src/app/organization/loan-provisioning-criteria/create-loan-provisioning-criteria/create-loan-provisioning-criteria.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -94,7 +99,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .cp-field { @@ -138,6 +145,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.html b/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.html index debb9e7de..6ba2f9c60 100644 --- a/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.html +++ b/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.html @@ -86,8 +86,8 @@ {{ 'labels.inputs.Liability Account' | translate }} {{ - (definition.liabilityAccount | find: loanProvisioningCriteriaAndTemplate.glAccounts : 'id' : 'name') || - '-' + (definition.liabilityAccount + | find: loanProvisioningCriteriaAndTemplate.glAccounts : 'id' : 'name') || '-' }} @@ -96,7 +96,8 @@ {{ 'labels.inputs.Expense Account' | translate }} {{ - (definition.expenseAccount | find: loanProvisioningCriteriaAndTemplate.glAccounts : 'id' : 'name') || '-' + (definition.expenseAccount + | find: loanProvisioningCriteriaAndTemplate.glAccounts : 'id' : 'name') || '-' }} diff --git a/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.scss b/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.scss index 71db39897..7c3ca112e 100644 --- a/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.scss +++ b/src/app/organization/loan-provisioning-criteria/edit-loan-provisioning-criteria/edit-loan-provisioning-criteria.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -94,7 +99,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ep-field { @@ -138,6 +145,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/loan-provisioning-criteria/loan-provisioning-criteria.component.html b/src/app/organization/loan-provisioning-criteria/loan-provisioning-criteria.component.html index 7f276c3d0..5f15fdf21 100644 --- a/src/app/organization/loan-provisioning-criteria/loan-provisioning-criteria.component.html +++ b/src/app/organization/loan-provisioning-criteria/loan-provisioning-criteria.component.html @@ -19,11 +19,21 @@
- +
- @@ -45,7 +55,12 @@ - + diff --git a/src/app/organization/manage-funds/create-fund/create-fund.component.html b/src/app/organization/manage-funds/create-fund/create-fund.component.html index d7f50764e..9d544d430 100644 --- a/src/app/organization/manage-funds/create-fund/create-fund.component.html +++ b/src/app/organization/manage-funds/create-fund/create-fund.component.html @@ -44,7 +44,14 @@ -
diff --git a/src/app/organization/manage-funds/create-fund/create-fund.component.scss b/src/app/organization/manage-funds/create-fund/create-fund.component.scss index d3e1492f4..a27f8e15a 100644 --- a/src/app/organization/manage-funds/create-fund/create-fund.component.scss +++ b/src/app/organization/manage-funds/create-fund/create-fund.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .cf-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .cf-field { @@ -115,6 +122,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/manage-funds/edit-fund/edit-fund.component.html b/src/app/organization/manage-funds/edit-fund/edit-fund.component.html index a68c76601..ac47104da 100644 --- a/src/app/organization/manage-funds/edit-fund/edit-fund.component.html +++ b/src/app/organization/manage-funds/edit-fund/edit-fund.component.html @@ -44,7 +44,14 @@ -
diff --git a/src/app/organization/manage-funds/edit-fund/edit-fund.component.scss b/src/app/organization/manage-funds/edit-fund/edit-fund.component.scss index 74b701ce6..5e54962b2 100644 --- a/src/app/organization/manage-funds/edit-fund/edit-fund.component.scss +++ b/src/app/organization/manage-funds/edit-fund/edit-fund.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .ef-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ef-field { @@ -115,6 +122,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/manage-funds/manage-funds.component.html b/src/app/organization/manage-funds/manage-funds.component.html index 157ef5d26..29b482b10 100644 --- a/src/app/organization/manage-funds/manage-funds.component.html +++ b/src/app/organization/manage-funds/manage-funds.component.html @@ -22,7 +22,13 @@
- diff --git a/src/app/organization/manage-funds/view-fund/view-fund.component.html b/src/app/organization/manage-funds/view-fund/view-fund.component.html index 5665706bd..ed7672aa3 100644 --- a/src/app/organization/manage-funds/view-fund/view-fund.component.html +++ b/src/app/organization/manage-funds/view-fund/view-fund.component.html @@ -13,7 +13,13 @@

{{ fundData.name }}

-
diff --git a/src/app/organization/manage-funds/view-fund/view-fund.component.scss b/src/app/organization/manage-funds/view-fund/view-fund.component.scss index 68d2f178a..e131487f6 100644 --- a/src/app/organization/manage-funds/view-fund/view-fund.component.scss +++ b/src/app/organization/manage-funds/view-fund/view-fund.component.scss @@ -45,7 +45,10 @@ $gray-900: #202124; height: 32px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } @@ -57,9 +60,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vf-card { @@ -98,7 +106,9 @@ $gray-900: #202124; gap: 16px; padding: 20px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vf-detail { diff --git a/src/app/organization/offices/create-office/create-office.component.html b/src/app/organization/offices/create-office/create-office.component.html index cf4c9ff6a..d0f0adc97 100644 --- a/src/app/organization/offices/create-office/create-office.component.html +++ b/src/app/organization/offices/create-office/create-office.component.html @@ -85,7 +85,14 @@ -
diff --git a/src/app/organization/offices/create-office/create-office.component.scss b/src/app/organization/offices/create-office/create-office.component.scss index fc39f327e..d62bbf71d 100644 --- a/src/app/organization/offices/create-office/create-office.component.scss +++ b/src/app/organization/offices/create-office/create-office.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .co-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/offices/edit-office/edit-office.component.html b/src/app/organization/offices/edit-office/edit-office.component.html index a94f3bac9..d91aaa5cb 100644 --- a/src/app/organization/offices/edit-office/edit-office.component.html +++ b/src/app/organization/offices/edit-office/edit-office.component.html @@ -87,7 +87,14 @@ -
diff --git a/src/app/organization/offices/edit-office/edit-office.component.scss b/src/app/organization/offices/edit-office/edit-office.component.scss index f42fefb5c..e6c0153fa 100644 --- a/src/app/organization/offices/edit-office/edit-office.component.scss +++ b/src/app/organization/offices/edit-office/edit-office.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .eo-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/offices/offices.component.html b/src/app/organization/offices/offices.component.html index c0dea616f..713500d70 100644 --- a/src/app/organization/offices/offices.component.html +++ b/src/app/organization/offices/offices.component.html @@ -32,13 +32,24 @@
-
- @@ -72,7 +83,12 @@ - +
diff --git a/src/app/organization/offices/view-office/general-tab/general-tab.component.html b/src/app/organization/offices/view-office/general-tab/general-tab.component.html index 127d3213d..3cb0f7f10 100644 --- a/src/app/organization/offices/view-office/general-tab/general-tab.component.html +++ b/src/app/organization/offices/view-office/general-tab/general-tab.component.html @@ -15,7 +15,9 @@
{{ 'labels.inputs.Opened On' | translate }}
-
{{ officeData.openingDate ? (officeData.openingDate | dateFormat) : 'Unassigned' }}
+
+ {{ officeData.openingDate ? (officeData.openingDate | dateFormat) : 'Unassigned' }} +
diff --git a/src/app/organization/offices/view-office/general-tab/general-tab.component.scss b/src/app/organization/offices/view-office/general-tab/general-tab.component.scss index c100fed1f..499c34073 100644 --- a/src/app/organization/offices/view-office/general-tab/general-tab.component.scss +++ b/src/app/organization/offices/view-office/general-tab/general-tab.component.scss @@ -25,14 +25,16 @@ $gray-900: #202124; grid-template-columns: repeat(2, 1fr); gap: 10px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .gt-cell { padding: 14px 16px; background: $gray-50; border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 4%); transition: background 0.12s; &:hover { diff --git a/src/app/organization/offices/view-office/view-office.component.html b/src/app/organization/offices/view-office/view-office.component.html index 406ce5c07..ac06a1c88 100644 --- a/src/app/organization/offices/view-office/view-office.component.html +++ b/src/app/organization/offices/view-office/view-office.component.html @@ -15,7 +15,13 @@

{{ 'labels.heading.Office Details' | translate }}

- diff --git a/src/app/organization/offices/view-office/view-office.component.scss b/src/app/organization/offices/view-office/view-office.component.scss index 8935c5007..b05fab471 100644 --- a/src/app/organization/offices/view-office/view-office.component.scss +++ b/src/app/organization/offices/view-office/view-office.component.scss @@ -52,9 +52,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vo-edit-btn { @@ -92,7 +97,7 @@ $gray-900: #202124; } // Responsive -@media (max-width: 600px) { +@media (width <= 600px) { .vo-header { flex-direction: column; align-items: flex-start; diff --git a/src/app/organization/organization.component.html b/src/app/organization/organization.component.html index 4e1dff96a..d3d116ce9 100644 --- a/src/app/organization/organization.component.html +++ b/src/app/organization/organization.component.html @@ -98,7 +98,9 @@

{{ 'labels.heading.Manage Loan Originators' | translate }}

-

{{ 'labels.text.Loan Originators are associated with loan accounts' | translate }}

+

+ {{ 'labels.text.Loan Originators are associated with loan accounts' | translate }} +

@@ -106,7 +108,11 @@

{{ 'labels.text.Investors' | translate }}

-

{{ 'labels.text.View the loan account details associated with the investors' | translate }}

+

+ {{ 'labels.text.View the loan account details associated with the investors' | translate }} +

@@ -190,7 +198,11 @@
- +
@@ -246,7 +258,11 @@

{{ 'labels.heading.Bulk Import' | translate }}

-

{{ 'labels.text.Bulk data import using excel spreadsheet templates' | translate }}

+

+ {{ 'labels.text.Bulk data import using excel spreadsheet templates' | translate }} +

diff --git a/src/app/organization/organization.component.scss b/src/app/organization/organization.component.scss index 11b969490..c2a8e640a 100644 --- a/src/app/organization/organization.component.scss +++ b/src/app/organization/organization.component.scss @@ -62,7 +62,7 @@ $gray-900: #0f172a; grid-template-columns: repeat(2, 1fr); gap: 12px; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: 1fr; } } @@ -83,7 +83,7 @@ $gray-900: #0f172a; &:hover { border-color: #cbd5e1; background: $gray-50; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + box-shadow: 0 2px 8px rgb(0 0 0 / 4%); .org-card__arrow { color: $gray-700; @@ -102,62 +102,62 @@ $gray-900: #0f172a; flex-shrink: 0; &--blue { - background: rgba(59, 130, 246, 0.1); + background: rgb(59 130 246 / 10%); color: #2563eb; } &--amber { - background: rgba(245, 158, 11, 0.1); + background: rgb(245 158 11 / 10%); color: #d97706; } &--green { - background: rgba(34, 197, 94, 0.1); + background: rgb(34 197 94 / 10%); color: #16a34a; } &--emerald { - background: rgba(16, 185, 129, 0.1); + background: rgb(16 185 129 / 10%); color: #059669; } &--purple { - background: rgba(139, 92, 246, 0.1); + background: rgb(139 92 246 / 10%); color: #7c3aed; } &--indigo { - background: rgba(99, 102, 241, 0.1); + background: rgb(99 102 241 / 10%); color: #4f46e5; } &--slate { - background: rgba(100, 116, 139, 0.1); + background: rgb(100 116 139 / 10%); color: #475569; } &--orange { - background: rgba(249, 115, 22, 0.1); + background: rgb(249 115 22 / 10%); color: #ea580c; } &--teal { - background: rgba(20, 184, 166, 0.1); + background: rgb(20 184 166 / 10%); color: #0d9488; } &--cyan { - background: rgba(6, 182, 212, 0.1); + background: rgb(6 182 212 / 10%); color: #0891b2; } &--rose { - background: rgba(244, 63, 94, 0.1); + background: rgb(244 63 94 / 10%); color: #e11d48; } &--red { - background: rgba(239, 68, 68, 0.1); + background: rgb(239 68 68 / 10%); color: #dc2626; } } @@ -196,8 +196,13 @@ $gray-900: #0f172a; // ── Dark mode ── :host-context(.dark-theme) { .org-header { - &__title { color: #e2e8f0; } - &__sub { color: #94a3b8; } + &__title { + color: #e2e8f0; + } + + &__sub { + color: #94a3b8; + } } .org-card { @@ -209,8 +214,16 @@ $gray-900: #0f172a; border-color: #444; } - &__title { color: #e2e8f0; } - &__desc { color: #64748b; } - &__arrow { color: #444; } + &__title { + color: #e2e8f0; + } + + &__desc { + color: #64748b; + } + + &__arrow { + color: #444; + } } } diff --git a/src/app/organization/organization.component.ts b/src/app/organization/organization.component.ts index 425f61a21..bc8508799 100644 --- a/src/app/organization/organization.component.ts +++ b/src/app/organization/organization.component.ts @@ -214,5 +214,4 @@ export class OrganizationComponent implements AfterViewInit { this.configurationWizardService.showRecurringDepositProductsList = true; this.router.navigate(['/products/recurring-deposit-products']); } - } diff --git a/src/app/organization/password-preferences/password-preferences.component.html b/src/app/organization/password-preferences/password-preferences.component.html index cb88133f4..6674be0af 100644 --- a/src/app/organization/password-preferences/password-preferences.component.html +++ b/src/app/organization/password-preferences/password-preferences.component.html @@ -22,14 +22,20 @@
-
+ [class.pp-option--selected]=" + passwordPreferencesForm.get('validationPolicyId')?.value === passwordPreference.id + " + >
{{ getPasswordLabel(passwordPreference) | translate }}
-
{{ passwordPreference.description | translateKey: 'passwordPreferences' }}
+
+ {{ passwordPreference.description | translateKey: 'passwordPreferences' }} +
@@ -39,8 +45,14 @@ -
diff --git a/src/app/organization/password-preferences/password-preferences.component.scss b/src/app/organization/password-preferences/password-preferences.component.scss index ce72a1dd6..3f607f820 100644 --- a/src/app/organization/password-preferences/password-preferences.component.scss +++ b/src/app/organization/password-preferences/password-preferences.component.scss @@ -14,48 +14,148 @@ $gray-500: #9aa0a6; $gray-700: #5f6368; $gray-900: #202124; -.pp-container { max-width: 700px; margin: 0 auto; padding: 20px 16px; } +.pp-container { + max-width: 700px; + margin: 0 auto; + padding: 20px 16px; +} .pp-header { - display: flex; align-items: center; gap: 14px; margin-bottom: 20px; - &__title { font-size: 18px; font-weight: 600; color: $gray-900; margin: 0; } -} + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 20px; -.pp-back { - border-radius: 8px !important; font-size: 12px !important; font-weight: 600 !important; - height: 32px !important; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } - &:hover { color: $primary !important; border-color: $primary !important; } -} - -.pp-card { background: #fff; border: 1px solid $border; border-radius: 10px; overflow: hidden; } - -.pp-section { - &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + &__title { + font-size: 18px; + font-weight: 600; + color: $gray-900; + margin: 0; } } -.pp-body { padding: 20px 24px; } +.pp-back { + border-radius: 8px !important; + font-size: 12px !important; + font-weight: 600 !important; + height: 32px !important; + border-color: $border !important; + color: $gray-500 !important; -.pp-options { display: flex; flex-direction: column; gap: 12px; } + i { + margin-right: 6px; + } + + &:hover { + color: $primary !important; + border-color: $primary !important; + } +} + +.pp-card { + background: #fff; + border: 1px solid $border; + border-radius: 10px; + overflow: hidden; +} + +.pp-section { + &__header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } + } +} + +.pp-body { + padding: 20px 24px; +} + +.pp-options { + display: flex; + flex-direction: column; + gap: 12px; +} .pp-option { - border: 1px solid $border; border-radius: 8px; padding: 16px 20px; + border: 1px solid $border; + border-radius: 8px; + padding: 16px 20px; transition: all 0.15s; - &:hover { border-color: #cbd5e1; background: $gray-50; } - &--selected { border-color: $primary; border-width: 2px; background: rgba($primary, 0.02); } - &__header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } - &__title { font-size: 14px; font-weight: 600; color: $gray-900; } - &__desc { font-size: 13px; color: $gray-700; line-height: 1.5; } + + &:hover { + border-color: #cbd5e1; + background: $gray-50; + } + + &--selected { + border-color: $primary; + border-width: 2px; + background: rgba($primary, 0.02); + } + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + } + + &__title { + font-size: 14px; + font-weight: 600; + color: $gray-900; + } + + &__desc { + font-size: 13px; + color: $gray-700; + line-height: 1.5; + } } .pp-actions { - display: flex; justify-content: flex-end; gap: 10px; padding: 14px 24px; - border-top: 1px solid $gray-200; background: $gray-50; - &__cancel { border-radius: 8px !important; font-size: 13px !important; font-weight: 600 !important; height: 36px !important; border-color: $border !important; color: $gray-700 !important; } - &__submit { border-radius: 8px !important; font-size: 13px !important; font-weight: 600 !important; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; i { margin-right: 6px; font-size: 12px; } } + display: flex; + justify-content: flex-end; + gap: 10px; + padding: 14px 24px; + border-top: 1px solid $gray-200; + background: $gray-50; + + &__cancel { + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + height: 36px !important; + border-color: $border !important; + color: $gray-700 !important; + } + + &__submit { + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + height: 36px !important; + box-shadow: 0 1px 3px rgba($primary, 0.3) !important; + + i { + margin-right: 6px; + font-size: 12px; + } + } } diff --git a/src/app/organization/payment-types/create-payment-type/create-payment-type.component.scss b/src/app/organization/payment-types/create-payment-type/create-payment-type.component.scss index 3788b976a..46ee5e127 100644 --- a/src/app/organization/payment-types/create-payment-type/create-payment-type.component.scss +++ b/src/app/organization/payment-types/create-payment-type/create-payment-type.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .cp-card { @@ -88,7 +93,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .cp-field { @@ -132,7 +139,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/payment-types/edit-payment-type/edit-payment-type.component.scss b/src/app/organization/payment-types/edit-payment-type/edit-payment-type.component.scss index cd6f9eb7d..43421c3e1 100644 --- a/src/app/organization/payment-types/edit-payment-type/edit-payment-type.component.scss +++ b/src/app/organization/payment-types/edit-payment-type/edit-payment-type.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .ep-card { @@ -88,7 +93,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .ep-field { @@ -132,7 +139,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/payment-types/payment-types.component.html b/src/app/organization/payment-types/payment-types.component.html index 1b0b7eec3..7cc16157c 100644 --- a/src/app/organization/payment-types/payment-types.component.html +++ b/src/app/organization/payment-types/payment-types.component.html @@ -21,7 +21,13 @@
- @@ -75,10 +81,12 @@ @if (!paymentType.isSystemDefined) { } diff --git a/src/app/organization/payment-types/payment-types.component.scss b/src/app/organization/payment-types/payment-types.component.scss index 246f03aff..0e73e74a1 100644 --- a/src/app/organization/payment-types/payment-types.component.scss +++ b/src/app/organization/payment-types/payment-types.component.scss @@ -33,14 +33,18 @@ $primary: #1353a0; background: #ecfdf5; color: #059669; - &::before { background: #059669; } + &::before { + background: #059669; + } } &--no { background: #f1f5f9; color: #94a3b8; - &::before { background: #94a3b8; } + &::before { + background: #94a3b8; + } } } @@ -54,5 +58,7 @@ $primary: #1353a0; color: #dc2626 !important; cursor: pointer; - &:hover { text-decoration: underline; } + &:hover { + text-decoration: underline; + } } diff --git a/src/app/organization/sms-campaigns/sms-campaigns.component.html b/src/app/organization/sms-campaigns/sms-campaigns.component.html index 9c571a69d..25557cbf7 100644 --- a/src/app/organization/sms-campaigns/sms-campaigns.component.html +++ b/src/app/organization/sms-campaigns/sms-campaigns.component.html @@ -19,11 +19,21 @@
- +
- diff --git a/src/app/organization/standing-instructions-history/standing-instructions-history.component.html b/src/app/organization/standing-instructions-history/standing-instructions-history.component.html index 29c7a788d..de20eab02 100644 --- a/src/app/organization/standing-instructions-history/standing-instructions-history.component.html +++ b/src/app/organization/standing-instructions-history/standing-instructions-history.component.html @@ -55,7 +55,13 @@ } {{ 'labels.inputs.From Date' | translate }} - + @@ -69,8 +75,16 @@
- - +
diff --git a/src/app/organization/standing-instructions-history/standing-instructions-history.component.scss b/src/app/organization/standing-instructions-history/standing-instructions-history.component.scss index 2d26ade30..95eb85351 100644 --- a/src/app/organization/standing-instructions-history/standing-instructions-history.component.scss +++ b/src/app/organization/standing-instructions-history/standing-instructions-history.component.scss @@ -59,7 +59,9 @@ $gray-900: #202124; border: 1px solid $border; border-radius: 12px; padding: 0; - box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06), 0 1px 2px rgba(16, 24, 40, 0.04); + box-shadow: + 0 1px 3px rgb(16 24 40 / 6%), + 0 1px 2px rgb(16 24 40 / 4%); } .si-section { @@ -91,7 +93,7 @@ $gray-900: #202124; grid-template-columns: 1fr 1fr; gap: 4px 20px; - @media (max-width: 600px) { + @media (width <= 600px) { grid-template-columns: 1fr; } } @@ -129,7 +131,7 @@ $gray-900: #202124; font-weight: 500; font-size: 13px; padding: 4px 20px !important; - box-shadow: 0 1px 2px rgba(19, 83, 160, 0.15); + box-shadow: 0 1px 2px rgb(19 83 160 / 15%); transition: all 0.2s ease; i { @@ -139,7 +141,7 @@ $gray-900: #202124; &:hover { background: #0f4688 !important; - box-shadow: 0 2px 6px rgba(19, 83, 160, 0.25); + box-shadow: 0 2px 6px rgb(19 83 160 / 25%); } } } @@ -150,7 +152,9 @@ $gray-900: #202124; border: 1px solid $border; border-radius: 12px; overflow: hidden; - box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06), 0 1px 2px rgba(16, 24, 40, 0.04); + box-shadow: + 0 1px 3px rgb(16 24 40 / 6%), + 0 1px 2px rgb(16 24 40 / 4%); } .modern-table { diff --git a/src/app/organization/tellers/cashiers/allocate-cash/allocate-cash.component.scss b/src/app/organization/tellers/cashiers/allocate-cash/allocate-cash.component.scss index 3e265191e..d9ad6c775 100644 --- a/src/app/organization/tellers/cashiers/allocate-cash/allocate-cash.component.scss +++ b/src/app/organization/tellers/cashiers/allocate-cash/allocate-cash.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .ac-card { @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .ac-field { @@ -123,7 +130,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/tellers/cashiers/cashiers.component.html b/src/app/organization/tellers/cashiers/cashiers.component.html index 3a82ba242..02ff07016 100644 --- a/src/app/organization/tellers/cashiers/cashiers.component.html +++ b/src/app/organization/tellers/cashiers/cashiers.component.html @@ -21,7 +21,13 @@
- diff --git a/src/app/organization/tellers/cashiers/cashiers.component.scss b/src/app/organization/tellers/cashiers/cashiers.component.scss index 5581482b1..39508d9bd 100644 --- a/src/app/organization/tellers/cashiers/cashiers.component.scss +++ b/src/app/organization/tellers/cashiers/cashiers.component.scss @@ -24,7 +24,9 @@ $primary: #1353a0; border-radius: 6px; font-size: 12px; cursor: pointer; - transition: opacity 0.15s, transform 0.15s; + transition: + opacity 0.15s, + transform 0.15s; text-decoration: none; &:hover { diff --git a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.html b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.html index b5bf17f5f..898cdb60c 100644 --- a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.html +++ b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.html @@ -105,7 +105,9 @@ @if (!createCashierForm.value.isFullDay) {
- {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) + {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) @for (hour of hours; track hour) { {{ hour }} @@ -113,7 +115,9 @@ - {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) + {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) @for (minute of minutes; track minute) { {{ minute }} @@ -123,7 +127,9 @@
- {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) + {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) @for (hour of hours; track hour) { {{ hour }} @@ -131,7 +137,9 @@ - {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) + {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) @for (minute of minutes; track minute) { {{ minute }} diff --git a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.scss b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.scss index a2ed21202..0edd30a90 100644 --- a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.scss +++ b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .cc-card { @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .cc-field { @@ -107,7 +114,10 @@ $gray-900: #202124; display: flex !important; gap: 12px; - @media (max-width: 600px) { flex-direction: column; gap: 4px; } + @media (width <= 600px) { + flex-direction: column; + gap: 4px; + } } .cc-time-field { @@ -148,7 +158,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html index 07bc437db..a1180f83e 100644 --- a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html +++ b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html @@ -112,7 +112,9 @@ @if (!editCashierForm.value.isFullDay) {
- {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) + {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) @for (hour of hours; track hour) { {{ hour }} @@ -120,7 +122,9 @@ - {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) + {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) @for (minute of minutes; track minute) { {{ minute }} @@ -130,7 +134,9 @@
- {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) + {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Hour' | translate }}) @for (hour of hours; track hour) { {{ hour }} @@ -138,7 +144,9 @@ - {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) + {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Minute' | translate }}) @for (minute of minutes; track minute) { {{ minute }} diff --git a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.scss b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.scss index 277c44fe8..641e7f1d9 100644 --- a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.scss +++ b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .ec-card { @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .ec-field { @@ -107,7 +114,10 @@ $gray-900: #202124; display: flex !important; gap: 12px; - @media (max-width: 600px) { flex-direction: column; gap: 4px; } + @media (width <= 600px) { + flex-direction: column; + gap: 4px; + } } .ec-time-field { @@ -148,7 +158,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/tellers/cashiers/settle-cash/settle-cash.component.scss b/src/app/organization/tellers/cashiers/settle-cash/settle-cash.component.scss index b15f002e9..4380cf2e2 100644 --- a/src/app/organization/tellers/cashiers/settle-cash/settle-cash.component.scss +++ b/src/app/organization/tellers/cashiers/settle-cash/settle-cash.component.scss @@ -46,9 +46,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .sc-card { @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .sc-field { @@ -123,7 +130,10 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } } diff --git a/src/app/organization/tellers/cashiers/transactions/transactions.component.html b/src/app/organization/tellers/cashiers/transactions/transactions.component.html index 2e2c95257..653db8587 100644 --- a/src/app/organization/tellers/cashiers/transactions/transactions.component.html +++ b/src/app/organization/tellers/cashiers/transactions/transactions.component.html @@ -16,11 +16,22 @@
- - @@ -44,19 +55,21 @@
{{ 'labels.inputs.Net Cash' | translate }} - {{ cashierData.netCash | formatNumber }} [{{ currencySelector.value }}] + {{ cashierData.netCash | formatNumber }} [{{ currencySelector.value }}]
} -
+
{{ 'labels.inputs.Filter' | translate }} - + {{ 'labels.inputs.Select Currency' | translate }} @for (currency of currencyData; track currency) { diff --git a/src/app/organization/tellers/cashiers/transactions/transactions.component.scss b/src/app/organization/tellers/cashiers/transactions/transactions.component.scss index 8952a97de..5188b6e53 100644 --- a/src/app/organization/tellers/cashiers/transactions/transactions.component.scss +++ b/src/app/organization/tellers/cashiers/transactions/transactions.component.scss @@ -27,7 +27,7 @@ $gray-900: #202124; border-radius: 10px; margin-bottom: 16px; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: repeat(2, 1fr); } diff --git a/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.html b/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.html index a8694867a..8ca681842 100644 --- a/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.html +++ b/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.html @@ -13,10 +13,22 @@

{{ cashierData.staffName }}

- -
diff --git a/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.scss b/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.scss index e945e341d..1f749f677 100644 --- a/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.scss +++ b/src/app/organization/tellers/cashiers/view-cashier/view-cashier.component.scss @@ -49,7 +49,10 @@ $gray-900: #202124; height: 32px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } &__delete { @@ -58,7 +61,10 @@ $gray-900: #202124; font-weight: 600 !important; height: 32px !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } @@ -70,9 +76,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vc-card { @@ -112,7 +123,9 @@ $gray-900: #202124; gap: 16px; padding: 20px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr !important; } + @media (width <= 600px) { + grid-template-columns: 1fr !important; + } } .vc-detail { diff --git a/src/app/organization/tellers/create-teller/create-teller.component.html b/src/app/organization/tellers/create-teller/create-teller.component.html index e5a794cc6..6c54655a0 100644 --- a/src/app/organization/tellers/create-teller/create-teller.component.html +++ b/src/app/organization/tellers/create-teller/create-teller.component.html @@ -117,7 +117,14 @@ -
diff --git a/src/app/organization/tellers/create-teller/create-teller.component.scss b/src/app/organization/tellers/create-teller/create-teller.component.scss index 8313d5bc3..1c40a1f45 100644 --- a/src/app/organization/tellers/create-teller/create-teller.component.scss +++ b/src/app/organization/tellers/create-teller/create-teller.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .ct-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ct-field { @@ -119,6 +126,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/tellers/edit-teller/edit-teller.component.html b/src/app/organization/tellers/edit-teller/edit-teller.component.html index d9657f786..9f6ded6da 100644 --- a/src/app/organization/tellers/edit-teller/edit-teller.component.html +++ b/src/app/organization/tellers/edit-teller/edit-teller.component.html @@ -111,7 +111,14 @@ -
diff --git a/src/app/organization/tellers/edit-teller/edit-teller.component.scss b/src/app/organization/tellers/edit-teller/edit-teller.component.scss index 2ae5f1468..97784ed96 100644 --- a/src/app/organization/tellers/edit-teller/edit-teller.component.scss +++ b/src/app/organization/tellers/edit-teller/edit-teller.component.scss @@ -43,9 +43,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .et-card { @@ -84,7 +89,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .et-field { @@ -119,6 +126,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/organization/tellers/tellers.component.html b/src/app/organization/tellers/tellers.component.html index 08fc0b6de..8e38d98f3 100644 --- a/src/app/organization/tellers/tellers.component.html +++ b/src/app/organization/tellers/tellers.component.html @@ -21,7 +21,13 @@
- @@ -45,7 +51,11 @@ {{ 'labels.inputs.Status' | translate }} - {{ teller.status === 'ACTIVE' ? ('labels.inputs.Active' | translate) : ('labels.catalogs.Inactive' | translate) }} + {{ + teller.status === 'ACTIVE' + ? ('labels.inputs.Active' | translate) + : ('labels.catalogs.Inactive' | translate) + }} @@ -59,7 +69,8 @@ {{ 'labels.inputs.Actions' | translate }} - {{ 'labels.buttons.View Cashiers' | translate }} + {{ 'labels.buttons.View Cashiers' | translate }} diff --git a/src/app/organization/tellers/view-teller/view-teller.component.html b/src/app/organization/tellers/view-teller/view-teller.component.html index 7258f9e1a..20fa9295e 100644 --- a/src/app/organization/tellers/view-teller/view-teller.component.html +++ b/src/app/organization/tellers/view-teller/view-teller.component.html @@ -13,10 +13,22 @@

{{ tellerData.name }}

- -
@@ -55,7 +67,9 @@
{{ 'labels.inputs.Status' | translate }} - + {{ tellerData.status | titlecase }} diff --git a/src/app/organization/tellers/view-teller/view-teller.component.scss b/src/app/organization/tellers/view-teller/view-teller.component.scss index 1017eea04..abefb3ae9 100644 --- a/src/app/organization/tellers/view-teller/view-teller.component.scss +++ b/src/app/organization/tellers/view-teller/view-teller.component.scss @@ -46,7 +46,10 @@ $gray-900: #202124; height: 32px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } &__delete { @@ -55,7 +58,10 @@ $gray-900: #202124; font-weight: 600 !important; height: 32px !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } @@ -67,9 +73,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } .vt-card { @@ -108,7 +119,9 @@ $gray-900: #202124; gap: 16px; padding: 20px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vt-detail { @@ -159,12 +172,16 @@ $gray-900: #202124; background: #ecfdf5; color: #059669; - &::before { background: #059669; } + &::before { + background: #059669; + } } .status-closed { background: #fef2f2; color: #dc2626; - &::before { background: #dc2626; } + &::before { + background: #dc2626; + } } diff --git a/src/app/organization/working-days/working-days.component.html b/src/app/organization/working-days/working-days.component.html index 7b9d6f18b..f1b05517c 100644 --- a/src/app/organization/working-days/working-days.component.html +++ b/src/app/organization/working-days/working-days.component.html @@ -1,3 +1,11 @@ + +
-
diff --git a/src/app/organization/working-days/working-days.component.scss b/src/app/organization/working-days/working-days.component.scss index 7d6ec5544..85d02648b 100644 --- a/src/app/organization/working-days/working-days.component.scss +++ b/src/app/organization/working-days/working-days.component.scss @@ -14,46 +14,123 @@ $gray-500: #9aa0a6; $gray-700: #5f6368; $gray-900: #202124; -.wd-container { max-width: 700px; margin: 0 auto; padding: 20px 16px; } +.wd-container { + max-width: 700px; + margin: 0 auto; + padding: 20px 16px; +} .wd-header { - display: flex; align-items: center; gap: 14px; margin-bottom: 20px; - &__title { font-size: 18px; font-weight: 600; color: $gray-900; margin: 0; } -} + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 20px; -.wd-back { - border-radius: 8px !important; font-size: 12px !important; font-weight: 600 !important; - height: 32px !important; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } - &:hover { color: $primary !important; border-color: $primary !important; } -} - -.wd-card { background: #fff; border: 1px solid $border; border-radius: 10px; overflow: hidden; } - -.wd-section { - &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + &__title { + font-size: 18px; + font-weight: 600; + color: $gray-900; + margin: 0; } } -.wd-body { padding: 20px 24px; } +.wd-back { + border-radius: 8px !important; + font-size: 12px !important; + font-weight: 600 !important; + height: 32px !important; + border-color: $border !important; + color: $gray-500 !important; + + i { + margin-right: 6px; + } + + &:hover { + color: $primary !important; + border-color: $primary !important; + } +} + +.wd-card { + background: #fff; + border: 1px solid $border; + border-radius: 10px; + overflow: hidden; +} + +.wd-section { + &__header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } + } +} + +.wd-body { + padding: 20px 24px; +} .wd-days { - display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 8px; } -.wd-day { padding: 8px 0; } +.wd-day { + padding: 8px 0; +} -.wd-field { width: 100%; } +.wd-field { + width: 100%; +} -.wd-checkbox { padding: 12px 0 4px; } +.wd-checkbox { + padding: 12px 0 4px; +} .wd-actions { - display: flex; justify-content: flex-end; gap: 10px; padding: 14px 24px; - border-top: 1px solid $gray-200; background: $gray-50; - &__cancel { border-radius: 8px !important; font-size: 13px !important; font-weight: 600 !important; height: 36px !important; border-color: $border !important; color: $gray-700 !important; } - &__submit { border-radius: 8px !important; font-size: 13px !important; font-weight: 600 !important; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; i { margin-right: 6px; font-size: 12px; } } + display: flex; + justify-content: flex-end; + gap: 10px; + padding: 14px 24px; + border-top: 1px solid $gray-200; + background: $gray-50; + + &__cancel { + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + height: 36px !important; + border-color: $border !important; + color: $gray-700 !important; + } + + &__submit { + border-radius: 8px !important; + font-size: 13px !important; + font-weight: 600 !important; + height: 36px !important; + box-shadow: 0 1px 3px rgba($primary, 0.3) !important; + + i { + margin-right: 6px; + font-size: 12px; + } + } } diff --git a/src/app/products/charges/charges.component.html b/src/app/products/charges/charges.component.html index e979b9928..7805863d1 100644 --- a/src/app/products/charges/charges.component.html +++ b/src/app/products/charges/charges.component.html @@ -34,7 +34,13 @@
- diff --git a/src/app/products/charges/create-charge/create-charge.component.html b/src/app/products/charges/create-charge/create-charge.component.html index 68dce2ef3..49633647e 100644 --- a/src/app/products/charges/create-charge/create-charge.component.html +++ b/src/app/products/charges/create-charge/create-charge.component.html @@ -207,8 +207,7 @@ } @if ( - (chargeForm.controls.feeInterval.hasError('min') || - chargeForm.controls.feeInterval.hasError('max')) && + (chargeForm.controls.feeInterval.hasError('min') || chargeForm.controls.feeInterval.hasError('max')) && !chargeForm.controls.feeInterval.hasError('pattern') ) { diff --git a/src/app/products/collaterals/collaterals.component.html b/src/app/products/collaterals/collaterals.component.html index 6f9427d9b..729d681c8 100644 --- a/src/app/products/collaterals/collaterals.component.html +++ b/src/app/products/collaterals/collaterals.component.html @@ -17,10 +17,20 @@
- +
- diff --git a/src/app/products/collaterals/create-collateral/create-collateral.component.html b/src/app/products/collaterals/create-collateral/create-collateral.component.html index 0c5802b68..07e8a1d6c 100644 --- a/src/app/products/collaterals/create-collateral/create-collateral.component.html +++ b/src/app/products/collaterals/create-collateral/create-collateral.component.html @@ -97,12 +97,7 @@ -
diff --git a/src/app/products/collaterals/edit-collateral/edit-collateral.component.html b/src/app/products/collaterals/edit-collateral/edit-collateral.component.html index f122419f7..414c46641 100644 --- a/src/app/products/collaterals/edit-collateral/edit-collateral.component.html +++ b/src/app/products/collaterals/edit-collateral/edit-collateral.component.html @@ -97,12 +97,7 @@ -
diff --git a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html index 2036fcd49..bc74f8541 100644 --- a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html +++ b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.html @@ -12,7 +12,12 @@

{{ 'labels.heading.Create Fixed Deposit Product' | translate }}

- + @@ -124,19 +129,19 @@
@if (selectedTabIndex > 0) { } @if (selectedTabIndex < totalTabs - 1) { } @if (fixedDepositProductFormValid && selectedTabIndex === totalTabs - 1) { } diff --git a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.scss b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.scss index 8e2ab2292..ce39f556f 100644 --- a/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.scss +++ b/src/app/products/fixed-deposit-products/create-fixed-deposit-product/create-fixed-deposit-product.component.scss @@ -8,33 +8,83 @@ $primary: #1353a0; -.fdp-container { max-width: 960px; margin: 0 auto; padding: 24px 16px; } - -.fdp-header { - display: flex; align-items: center; gap: 10px; margin-bottom: 20px; - &__icon { font-size: 20px; color: $primary; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } +.fdp-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; } -.fdp-tabs { - background: var(--md-sys-color-surface, #fff); border-radius: 8px; - box-shadow: 0 2px 8px rgb(0 0 0 / 8%); +.fdp-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; - ::ng-deep { - .mat-mdc-tab-header { border-bottom: 1px solid rgb(0 0 0 / 8%); } - .mat-mdc-tab { min-width: 100px; padding: 0 12px; height: 52px; } - .mat-mdc-tab-body-wrapper { min-height: 200px; } - .mat-mdc-tab-body { overflow: visible !important; } - .mat-mdc-tab-body-content { overflow: visible !important; } + &__icon { + font-size: 20px; + color: $primary; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; } } -.fdp-tab-icon { margin-right: 8px; font-size: 14px; } -.fdp-tab-content { padding: 24px 16px; } +.fdp-tabs { + background: var(--md-sys-color-surface, #fff); + border-radius: 8px; + box-shadow: 0 2px 8px rgb(0 0 0 / 8%); + + ::ng-deep { + .mat-mdc-tab-header { + border-bottom: 1px solid rgb(0 0 0 / 8%); + } + + .mat-mdc-tab { + min-width: 100px; + padding: 0 12px; + height: 52px; + } + + .mat-mdc-tab-body-wrapper { + min-height: 200px; + } + + .mat-mdc-tab-body { + overflow: visible !important; + } + + .mat-mdc-tab-body-content { + overflow: visible !important; + } + } +} + +.fdp-tab-icon { + margin-right: 8px; + font-size: 14px; +} + +.fdp-tab-content { + padding: 24px 16px; +} .fdp-action-bar { - display: flex; justify-content: space-between; align-items: center; - margin-top: 20px; padding: 16px 0; border-top: 1px solid rgb(0 0 0 / 8%); - &__cancel { color: rgb(0 0 0 / 60%); } - &__right { display: flex; gap: 12px; } + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 20px; + padding: 16px 0; + border-top: 1px solid rgb(0 0 0 / 8%); + + &__cancel { + color: rgb(0 0 0 / 60%); + } + + &__right { + display: flex; + gap: 12px; + } } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html index 782cd2f88..86939918d 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-accounting-step/fixed-deposit-product-accounting-step.component.html @@ -169,10 +169,7 @@
@if (paymentChannelToFundSourceMappings.value.length !== 0) {
- +
{{ 'labels.heading.Payment Type' | translate }} diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.html index 02fadc1c6..a8de6dfc4 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.html @@ -24,7 +24,14 @@
- @@ -33,10 +40,7 @@ @if (chargesDataSource.length > 0) {
- +
{{ 'labels.inputs.Name' | translate }} diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component.html index 24e12e096..8620c0951 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-interest-rate-chart-step/fixed-deposit-product-interest-rate-chart-step.component.html @@ -78,7 +78,13 @@ {{ 'labels.inputs.End Date' | translate }} - + @@ -91,7 +97,13 @@
- @@ -105,11 +117,7 @@ @if (chart.value.chartSlabs.length !== 0) {
- +
@@ -82,26 +91,60 @@

{{ 'labels.buttons.Create User' | translate }}

{{ 'labels.text.This option allows you to create new users in your organization' | translate }}.

- - - + + +

{{ 'labels.heading.Search bar to filter users' | translate }}.

- - - + + +

{{ 'labels.heading.List of Users with their details' | translate }}.

- - - + + +
diff --git a/src/app/users/users.component.scss b/src/app/users/users.component.scss index 237d4d20a..617145bd3 100644 --- a/src/app/users/users.component.scss +++ b/src/app/users/users.component.scss @@ -12,12 +12,16 @@ background: #ecfdf5 !important; color: #059669 !important; - &::before { background: #059669; } + &::before { + background: #059669; + } } .status-inactive { background: #fef2f2 !important; color: #dc2626 !important; - &::before { background: #dc2626; } + &::before { + background: #dc2626; + } } diff --git a/src/app/users/view-user/view-user.component.html b/src/app/users/view-user/view-user.component.html index 9059927d4..aea1e1c6c 100644 --- a/src/app/users/view-user/view-user.component.html +++ b/src/app/users/view-user/view-user.component.html @@ -34,9 +34,15 @@ {{ role.name }} } - + {{ 'labels.buttons.Self Service' | translate }} - {{ userData.selfService ? ('labels.buttons.Yes' | translate) : ('labels.buttons.No' | translate) }} + {{ + userData.selfService ? ('labels.buttons.Yes' | translate) : ('labels.buttons.No' | translate) + }} @@ -78,7 +84,11 @@
{{ 'labels.buttons.Is Self Service' | translate }}
- + {{ userData.selfService ? ('labels.buttons.Yes' | translate) : ('labels.buttons.No' | translate) }} diff --git a/src/app/users/view-user/view-user.component.scss b/src/app/users/view-user/view-user.component.scss index 30920aae0..1811af90e 100644 --- a/src/app/users/view-user/view-user.component.scss +++ b/src/app/users/view-user/view-user.component.scss @@ -22,8 +22,8 @@ $accent: #0ea5e9; background: #fff; border-radius: 10px; overflow: hidden; - border: 1px solid rgba(0, 0, 0, 0.06); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 6%); + box-shadow: 0 1px 4px rgb(0 0 0 / 4%); } // ── Dark gradient header ── @@ -39,14 +39,18 @@ $accent: #0ea5e9; &::after { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: repeating-linear-gradient(108deg, transparent 0, transparent 80px, rgb(255 255 255 / 2.5%) 80px, rgb(255 255 255 / 2.5%) 160px); + inset: 0; + background: repeating-linear-gradient( + 108deg, + transparent 0, + transparent 80px, + rgb(255 255 255 / 2.5%) 80px, + rgb(255 255 255 / 2.5%) 160px + ); pointer-events: none; } + /* stylelint-disable-next-line selector-max-universal */ > * { position: relative; z-index: 1; @@ -59,7 +63,7 @@ $accent: #0ea5e9; &__icon { font-size: 56px; - color: rgba(255, 255, 255, 0.25); + color: rgb(255 255 255 / 25%); } } @@ -92,23 +96,23 @@ $accent: #0ea5e9; &__username { font-size: 13px; font-weight: 500; - color: rgba(255, 255, 255, 0.5); - font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; + color: rgb(255 255 255 / 50%); + font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; } } .vu-menu-btn { - color: rgba(255, 255, 255, 0.7) !important; + color: rgb(255 255 255 / 70%) !important; width: 28px !important; height: 28px !important; min-width: 28px !important; padding: 0 !important; - border: 1px solid rgba(255, 255, 255, 0.15); + border: 1px solid rgb(255 255 255 / 15%); border-radius: 5px !important; line-height: 28px !important; &:hover { - background: rgba(255, 255, 255, 0.1); + background: rgb(255 255 255 / 10%); color: #fff !important; } } @@ -125,14 +129,14 @@ $accent: #0ea5e9; align-items: center; gap: 8px; padding: 4px 11px; - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.13); + background: rgb(255 255 255 / 10%); + border: 1px solid rgb(255 255 255 / 13%); border-radius: 5px; &__label { font-size: 9px; font-weight: 700; - color: rgba(255, 255, 255, 0.4); + color: rgb(255 255 255 / 40%); text-transform: uppercase; letter-spacing: 0.4px; } @@ -144,17 +148,21 @@ $accent: #0ea5e9; } &--active { - background: rgba(16, 185, 129, 0.15); - border-color: rgba(16, 185, 129, 0.25); + background: rgb(16 185 129 / 15%); + border-color: rgb(16 185 129 / 25%); - .vu-chip__val { color: #6ee7b7; } + .vu-chip__val { + color: #6ee7b7; + } } &--inactive { - background: rgba(239, 68, 68, 0.12); - border-color: rgba(239, 68, 68, 0.2); + background: rgb(239 68 68 / 12%); + border-color: rgb(239 68 68 / 20%); - .vu-chip__val { color: #fca5a5; } + .vu-chip__val { + color: #fca5a5; + } } } @@ -171,7 +179,7 @@ $accent: #0ea5e9; padding: 14px 16px; background: #f8fafc; border-radius: 8px; - border: 1px solid rgba(0, 0, 0, 0.04); + border: 1px solid rgb(0 0 0 / 4%); margin: 3px; transition: background 0.12s; @@ -224,13 +232,17 @@ $accent: #0ea5e9; &--yes { color: #16a34a; - .vu-status__dot { background: #16a34a; } + .vu-status__dot { + background: #16a34a; + } } &--no { color: #dc2626; - .vu-status__dot { background: #dc2626; } + .vu-status__dot { + background: #dc2626; + } } } @@ -269,20 +281,30 @@ $accent: #0ea5e9; } // ── Responsive ── -@media (max-width: 768px) { +@media (width <= 768px) { .vu-top { flex-direction: column; align-items: center; text-align: center; } - .vu-name-row { justify-content: center; } - .vu-chips { justify-content: center; } - .vu-grid { grid-template-columns: repeat(2, 1fr); } + .vu-name-row { + justify-content: center; + } + + .vu-chips { + justify-content: center; + } + + .vu-grid { + grid-template-columns: repeat(2, 1fr); + } } -@media (max-width: 480px) { - .vu-grid { grid-template-columns: 1fr; } +@media (width <= 480px) { + .vu-grid { + grid-template-columns: 1fr; + } } // ── Dark mode ── @@ -298,12 +320,19 @@ $accent: #0ea5e9; .vu-cell { background: #2a2a2a; - border-color: rgba(255, 255, 255, 0.06); + border-color: rgb(255 255 255 / 6%); - &:hover { background: #333; } + &:hover { + background: #333; + } - &__label { color: #888; } - &__value { color: #e0e0e0; } + &__label { + color: #888; + } + + &__value { + color: #e0e0e0; + } } .vu-role-tag { diff --git a/src/app/web-app.component.ts b/src/app/web-app.component.ts index a3ccae8c6..b6c9e2445 100644 --- a/src/app/web-app.component.ts +++ b/src/app/web-app.component.ts @@ -188,21 +188,30 @@ export class WebAppComponent implements OnInit, OnDestroy { ) .subscribe((event) => { if (!event['title']) { - this.i18nService.translate('APP_NAME').pipe(take(1)).subscribe((t: any) => this.titleService.setTitle(t)); + this.i18nService + .translate('APP_NAME') + .pipe(take(1)) + .subscribe((t: any) => this.titleService.setTitle(t)); return; } const raw = event['title']; const textKey = `labels.text.${raw}`; const menuKey = `labels.menus.${raw}`; - this.i18nService.translate(textKey).pipe(take(1)).subscribe((textResult: any) => { - if (textResult !== textKey) { - this.titleService.setTitle(textResult); - } else { - this.i18nService.translate(menuKey).pipe(take(1)).subscribe((menuResult: any) => { - this.titleService.setTitle(menuResult !== menuKey ? menuResult : raw); - }); - } - }); + this.i18nService + .translate(textKey) + .pipe(take(1)) + .subscribe((textResult: any) => { + if (textResult !== textKey) { + this.titleService.setTitle(textResult); + } else { + this.i18nService + .translate(menuKey) + .pipe(take(1)) + .subscribe((menuResult: any) => { + this.titleService.setTitle(menuResult !== menuKey ? menuResult : raw); + }); + } + }); }); // Stores top 100 user activites as local storage object. diff --git a/src/assets/images/cic-cibil.svg b/src/assets/images/cic-cibil.svg new file mode 100644 index 000000000..26e0e7851 --- /dev/null +++ b/src/assets/images/cic-cibil.svg @@ -0,0 +1,6 @@ + + + Trans + Union + CIBIL + diff --git a/src/assets/images/cic-crif.svg b/src/assets/images/cic-crif.svg new file mode 100644 index 000000000..9fad932f6 --- /dev/null +++ b/src/assets/images/cic-crif.svg @@ -0,0 +1,5 @@ + + + CRIF + HIGH MARK + diff --git a/src/assets/images/cic-equifax.svg b/src/assets/images/cic-equifax.svg new file mode 100644 index 000000000..4532308ac --- /dev/null +++ b/src/assets/images/cic-equifax.svg @@ -0,0 +1,5 @@ + + + EFX + + diff --git a/src/assets/images/cic-experian.svg b/src/assets/images/cic-experian.svg new file mode 100644 index 000000000..0be8f7373 --- /dev/null +++ b/src/assets/images/cic-experian.svg @@ -0,0 +1,5 @@ + + + EXP + + diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 8112c07fa..92ff5e0ad 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -123,8 +123,7 @@ export const environment = { loadedEnv['productionModeEnableRBAC'] === 'true' || loadedEnv['productionModeEnableRBAC'] === true || false, /** OpenProject Integration (via Fineract backend) */ - openProjectEnabled: - loadedEnv['openProjectEnabled'] === 'true' || loadedEnv['openProjectEnabled'] === true || false, + openProjectEnabled: loadedEnv['openProjectEnabled'] === 'true' || loadedEnv['openProjectEnabled'] === true || false, /** CFCFRMS (Cyber Fraud Complaint) Integration */ cfcfrmsApiUrl: loadedEnv['cfcfrmsApiUrl'] || '',
{{ 'labels.inputs.Period' | translate }} @@ -182,7 +190,9 @@ >

{{ 'labels.heading.Incentives' | translate }}

@@ -201,25 +211,33 @@
- + - + - + - + - + diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html index 2fb3e363c..716482fcf 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-preview-step/fixed-deposit-product-preview-step.component.html @@ -299,7 +299,9 @@ >

{{ 'labels.heading.Incentives' | translate }}

@@ -310,36 +312,54 @@
- + - + - + - + @@ -452,7 +480,10 @@
@if (!isNoneAccounting()) { - + }
@@ -474,10 +505,7 @@ @if (fixedDepositProduct.paymentChannelToFundSourceMappings?.length) {

{{ 'labels.heading.Fund Sources for Payment Channels' | translate }}

-
{{ 'labels.inputs.Entity Type' | translate }} + {{ 'labels.inputs.Entity Type' | translate }} + {{ incentive.entityType | find: entityTypeData : 'id' : 'value' }} {{ 'labels.inputs.Attribute Name' | translate }} + {{ 'labels.inputs.Attribute Name' | translate }} + {{ incentive.attributeName | find: attributeNameData : 'id' : 'value' }} {{ 'labels.inputs.Condition Type' | translate }} + {{ 'labels.inputs.Condition Type' | translate }} + {{ incentive.conditionType | find: conditionTypeData : 'id' : 'value' }} {{ 'labels.inputs.Attribute Value' | translate }} + {{ 'labels.inputs.Attribute Value' | translate }} + @switch (incentive.attributeName) { @case (2) { @@ -229,7 +247,9 @@ {{ incentive.attributeValue }} } @case (4) { - {{ incentive.attributeValue | find: clientTypeData : 'id' : 'name' }} + {{ + incentive.attributeValue | find: clientTypeData : 'id' : 'name' + }} } @case (5) { {{ @@ -240,7 +260,9 @@ {{ 'labels.inputs.Incentive Type' | translate }} + {{ 'labels.inputs.Incentive Type' | translate }} + {{ incentive.incentiveType | find: incentiveTypeData : 'id' : 'value' }} {{ incentive.entityType - | find: fixedDepositProductsTemplate.chartTemplate.entityTypeOptions : 'id' : 'value' + | find + : fixedDepositProductsTemplate.chartTemplate.entityTypeOptions + : 'id' + : 'value' }} {{ 'labels.inputs.Attribute Name' | translate }} + {{ 'labels.inputs.Attribute Name' | translate }} + {{ incentive.attributeName - | find: fixedDepositProductsTemplate.chartTemplate.attributeNameOptions : 'id' : 'value' + | find + : fixedDepositProductsTemplate.chartTemplate.attributeNameOptions + : 'id' + : 'value' }} {{ 'labels.inputs.Condition Type' | translate }} + {{ 'labels.inputs.Condition Type' | translate }} + {{ incentive.conditionType - | find: fixedDepositProductsTemplate.chartTemplate.conditionTypeOptions : 'id' : 'value' + | find + : fixedDepositProductsTemplate.chartTemplate.conditionTypeOptions + : 'id' + : 'value' }} {{ 'labels.inputs.Attribute Value' | translate }} + {{ 'labels.inputs.Attribute Value' | translate }} + @switch (incentive.attributeName) { @case (2) { {{ incentive.attributeValue - | find: fixedDepositProductsTemplate.chartTemplate.genderOptions : 'id' : 'name' + | find + : fixedDepositProductsTemplate.chartTemplate.genderOptions + : 'id' + : 'name' }} } @case (3) { @@ -348,7 +368,10 @@ @case (4) { {{ incentive.attributeValue - | find: fixedDepositProductsTemplate.chartTemplate.clientTypeOptions : 'id' : 'name' + | find + : fixedDepositProductsTemplate.chartTemplate.clientTypeOptions + : 'id' + : 'name' }} } @case (5) { @@ -364,11 +387,16 @@ {{ 'labels.inputs.Incentive Type' | translate }} + {{ 'labels.inputs.Incentive Type' | translate }} + {{ incentive.incentiveType - | find: fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions : 'id' : 'value' + | find + : fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions + : 'id' + : 'value' }}
+
- +
{{ 'labels.inputs.Payment Type' | translate }} @@ -504,10 +532,7 @@ @if (fixedDepositProduct.feeToIncomeAccountMappings?.length) {

{{ 'labels.heading.Fees to Specific Income Accounts' | translate }}

- +
@@ -90,7 +90,12 @@ - +
{{ 'labels.inputs.Fees' | translate }} @@ -529,12 +554,11 @@ } @if (fixedDepositProduct.penaltyToIncomeAccountMappings?.length) { -

{{ 'labels.heading.Penalties to Specific Income Accounts' | translate }}

+

+ {{ 'labels.heading.Penalties to Specific Income Accounts' | translate }} +

- +
+ diff --git a/src/app/products/floating-rates/floating-rates.component.html b/src/app/products/floating-rates/floating-rates.component.html index 1f4a494be..ebab660bc 100644 --- a/src/app/products/floating-rates/floating-rates.component.html +++ b/src/app/products/floating-rates/floating-rates.component.html @@ -24,7 +24,13 @@
- diff --git a/src/app/products/loan-products/create-loan-product/create-loan-product.component.html b/src/app/products/loan-products/create-loan-product/create-loan-product.component.html index 44f05534d..260e066d8 100644 --- a/src/app/products/loan-products/create-loan-product/create-loan-product.component.html +++ b/src/app/products/loan-products/create-loan-product/create-loan-product.component.html @@ -12,7 +12,12 @@

{{ 'labels.heading.Create Loan Product' | translate }}

- + @@ -170,19 +175,19 @@
@if (selectedTabIndex > 0) { } @if (selectedTabIndex < totalTabs - 1) { } @if (loanProductFormValid && selectedTabIndex === totalTabs - 1) { } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html index 7ecf2adef..61943160b 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html @@ -7,7 +7,6 @@ --> -
@@ -16,10 +15,7 @@
- + @for (accountingRule of accountingRuleData; track accountingRule; let i = $index) { {{ 'labels.accounting.' + accountingRule | translate }} @@ -38,7 +34,6 @@
@if (loanProductAccountingForm.value.accountingRule >= 2 && loanProductAccountingForm.value.accountingRule <= 4) { -
@@ -475,10 +470,7 @@
@if (chargeOffReasonToExpenseAccountMappings.value.length !== 0) {
-
{{ 'labels.inputs.Penalty' | translate }} diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.html index 37e6b07b2..5c8f66550 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-settings-step/fixed-deposit-product-settings-step.component.html @@ -160,7 +160,10 @@ {{ 'labels.inputs.Period' | translate }} - @for (preClosurePenalInterestOnType of preClosurePenalInterestOnTypeData; track preClosurePenalInterestOnType) { + @for ( + preClosurePenalInterestOnType of preClosurePenalInterestOnTypeData; + track preClosurePenalInterestOnType + ) { {{ preClosurePenalInterestOnType.value }} diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html index 448540345..c2228368b 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-terms-step/fixed-deposit-product-terms-step.component.html @@ -66,7 +66,10 @@ matTooltip="{{ 'tooltips.The period at which interest rate' | translate }}" required > - @for (interestCompoundingPeriodType of interestCompoundingPeriodTypeData; track interestCompoundingPeriodType) { + @for ( + interestCompoundingPeriodType of interestCompoundingPeriodTypeData; + track interestCompoundingPeriodType + ) { {{ interestCompoundingPeriodType.value }} diff --git a/src/app/products/floating-rates/create-floating-rate/create-floating-rate.component.html b/src/app/products/floating-rates/create-floating-rate/create-floating-rate.component.html index a6d4948f3..c60da248c 100644 --- a/src/app/products/floating-rates/create-floating-rate/create-floating-rate.component.html +++ b/src/app/products/floating-rates/create-floating-rate/create-floating-rate.component.html @@ -88,7 +88,9 @@ > {{ 'labels.inputs.Interest Rate' | translate }} - {{ floatingRatePeriod.interestRate | formatNumber }} + {{ floatingRatePeriod.interestRate | formatNumber }} +
+
- +
{{ 'labels.inputs.Charge-off reason' | translate }} @@ -552,7 +544,5 @@ > } - } - diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-capitalized-income-step/loan-product-deferred-income-recognition-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-capitalized-income-step/loan-product-deferred-income-recognition-step.component.scss index 3b4fc2e89..65975ad97 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-capitalized-income-step/loan-product-deferred-income-recognition-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-capitalized-income-step/loan-product-deferred-income-recognition-step.component.scss @@ -18,7 +18,9 @@ $gray-900: #202124; overflow: hidden; background: #fff; - & + & { margin-top: 16px; } + & + & { + margin-top: 16px; + } &__header { display: flex; @@ -28,8 +30,17 @@ $gray-900: #202124; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } @@ -39,12 +50,17 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .lpdi-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } .lpdi-checkbox-row { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.html index c4763afa7..ec1e817bf 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.html @@ -43,11 +43,7 @@ @if ((chargesDataSource | chargesPenaltyFilter: false).length > 0) {
- +
@@ -203,7 +249,9 @@ - + @@ -219,7 +267,9 @@ - + diff --git a/src/app/system/audit-trails/audit-trails.component.scss b/src/app/system/audit-trails/audit-trails.component.scss index 3554b8d59..3963eeae5 100644 --- a/src/app/system/audit-trails/audit-trails.component.scss +++ b/src/app/system/audit-trails/audit-trails.component.scss @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +@use 'sass:color'; @use '../../shared/styles/list-layout' as *; $blue: #1353a0; @@ -90,7 +91,7 @@ $muted: #64748b; box-shadow: 0 1px 4px rgb(19 83 160 / 25%); &:hover { - background: darken($blue, 6%); + background: color.adjust($blue, $lightness: -6%); } } } @@ -263,7 +264,7 @@ $muted: #64748b; } // ===== Responsive ===== -@media (max-width: 960px) { +@media (width <= 960px) { .at-filters__row--3 { grid-template-columns: repeat(2, 1fr); } @@ -278,7 +279,7 @@ $muted: #64748b; } } -@media (max-width: 600px) { +@media (width <= 600px) { .at-filters__row--3 { grid-template-columns: 1fr; } diff --git a/src/app/system/codes/codes.component.html b/src/app/system/codes/codes.component.html index 5646f1ce4..8530ca49f 100644 --- a/src/app/system/codes/codes.component.html +++ b/src/app/system/codes/codes.component.html @@ -24,7 +24,13 @@
- diff --git a/src/app/system/codes/create-code/create-code.component.scss b/src/app/system/codes/create-code/create-code.component.scss index 78e826641..6b03edc7b 100644 --- a/src/app/system/codes/create-code/create-code.component.scss +++ b/src/app/system/codes/create-code/create-code.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .cc-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/system/codes/edit-code/edit-code.component.scss b/src/app/system/codes/edit-code/edit-code.component.scss index 71c7cef10..2552f62e0 100644 --- a/src/app/system/codes/edit-code/edit-code.component.scss +++ b/src/app/system/codes/edit-code/edit-code.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ec-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/system/codes/view-code/view-code.component.html b/src/app/system/codes/view-code/view-code.component.html index a252e7c19..80160fe68 100644 --- a/src/app/system/codes/view-code/view-code.component.html +++ b/src/app/system/codes/view-code/view-code.component.html @@ -17,13 +17,25 @@
@if (!codeData.systemDefined) { - } @if (!codeData.systemDefined) { - @@ -88,11 +100,20 @@ @for (codeValue of codeValues.controls; track codeValue; let i = $index) { -
+
- + @if (codeValues.at(i).controls.name.hasError('required')) { {{ 'labels.inputs.name' | translate }} {{ 'labels.commons.is' | translate }} @@ -103,12 +124,23 @@
- +
- + @if (codeValues.at(i).controls.position.hasError('required')) { {{ 'labels.inputs.Position' | translate }} {{ 'labels.commons.is' | translate }} diff --git a/src/app/system/codes/view-code/view-code.component.scss b/src/app/system/codes/view-code/view-code.component.scss index 4b206c4df..949e5145f 100644 --- a/src/app/system/codes/view-code/view-code.component.scss +++ b/src/app/system/codes/view-code/view-code.component.scss @@ -73,9 +73,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -125,7 +130,9 @@ $gray-900: #202124; padding: 10px 0; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } &__label { flex: 0 0 40%; @@ -149,10 +156,17 @@ $gray-900: #202124; font-weight: 600; font-size: 13px; - i { font-size: 11px; } + i { + font-size: 11px; + } - &--yes { color: #16a34a; } - &--no { color: #dc2626; } + &--yes { + color: #16a34a; + } + + &--no { + color: #dc2626; + } } // Add button in section header @@ -192,9 +206,17 @@ $gray-900: #202124; min-width: 700px; transition: background 0.1s; - &:last-child { border-bottom: none; } - &:nth-child(even) { background: #fafbfc; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:nth-child(even) { + background: #fafbfc; + } + + &:hover { + background: #f8fafc; + } &--editing { background: #eff6ff !important; @@ -208,10 +230,26 @@ $gray-900: #202124; } .vc-values-col { - &--name { flex: 0 0 22%; padding-right: 8px; } - &--desc { flex: 0 0 26%; padding-right: 8px; } - &--pos { flex: 0 0 14%; padding-right: 8px; } - &--active { flex: 0 0 12%; padding-right: 8px; } + &--name { + flex: 0 0 22%; + padding-right: 8px; + } + + &--desc { + flex: 0 0 26%; + padding-right: 8px; + } + + &--pos { + flex: 0 0 14%; + padding-right: 8px; + } + + &--active { + flex: 0 0 12%; + padding-right: 8px; + } + &--actions { flex: 0 0 26%; display: flex; @@ -225,7 +263,9 @@ $gray-900: #202124; width: 100%; ::ng-deep { - .mat-mdc-form-field-flex { height: 36px; } + .mat-mdc-form-field-flex { + height: 36px; + } .mat-mdc-form-field-infix { padding: 4px 0; @@ -233,11 +273,18 @@ $gray-900: #202124; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 6px 0 0 6px; } - .mdc-notched-outline__trailing { border-radius: 0 6px 6px 0; } + .mdc-notched-outline__leading { + border-radius: 6px 0 0 6px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 6px 6px 0; + } } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -272,7 +319,7 @@ $gray-900: #202124; } // Responsive -@media (max-width: 768px) { +@media (width <= 768px) { .vc-header { flex-direction: column; align-items: flex-start; diff --git a/src/app/system/roles-and-permissions/add-role/add-role.component.scss b/src/app/system/roles-and-permissions/add-role/add-role.component.scss index 143f8221c..4578bb785 100644 --- a/src/app/system/roles-and-permissions/add-role/add-role.component.scss +++ b/src/app/system/roles-and-permissions/add-role/add-role.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ar-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/system/roles-and-permissions/edit-role/edit-role.component.html b/src/app/system/roles-and-permissions/edit-role/edit-role.component.html index 00082819d..081f534de 100644 --- a/src/app/system/roles-and-permissions/edit-role/edit-role.component.html +++ b/src/app/system/roles-and-permissions/edit-role/edit-role.component.html @@ -48,7 +48,13 @@ -
diff --git a/src/app/system/roles-and-permissions/edit-role/edit-role.component.scss b/src/app/system/roles-and-permissions/edit-role/edit-role.component.scss index e2407e189..70292ff84 100644 --- a/src/app/system/roles-and-permissions/edit-role/edit-role.component.scss +++ b/src/app/system/roles-and-permissions/edit-role/edit-role.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .er-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/system/roles-and-permissions/view-role/view-role.component.html b/src/app/system/roles-and-permissions/view-role/view-role.component.html index ef64984ce..f1842258b 100644 --- a/src/app/system/roles-and-permissions/view-role/view-role.component.html +++ b/src/app/system/roles-and-permissions/view-role/view-role.component.html @@ -19,31 +19,45 @@
- @if (!isRoleEnable(rolePermissionService.disabled)) { - } @if (isRoleEnable(rolePermissionService.disabled)) { - } - @@ -54,7 +68,9 @@
-

{{ 'labels.buttons.Permissions' | translate }}: {{ formatName(previousGrouping) }}

+

+ {{ 'labels.buttons.Permissions' | translate }}: {{ formatName(previousGrouping) }} +

@if (!isDisabled) {
- } - +
@if (group.expanded) {
@for (cat of group.categories; track cat) { -
+ (click)="showPermissions(cat)" + > {{ formatName(cat) }} @if (!isDisabled) { - - } @@ -113,20 +151,33 @@
- @if (permissionSubGroups.length > 0) { @for (subGroup of permissionSubGroups; track subGroup.entity) {
- + {{ getEntityDisplayName(subGroup.entity) }} {{ getSubGroupCount(subGroup) }} @if (!isDisabled) { - - } @@ -137,10 +188,20 @@
Maker @if (!isDisabled && subGroup.makerPerms.length > 0) { - - } @@ -162,10 +223,20 @@
Checker @if (!isDisabled && subGroup.checkerPerms.length > 0) { - - } @@ -194,10 +265,20 @@
Maker @if (!isDisabled && flatMakerPerms.length > 0) { - - } @@ -216,10 +297,20 @@
Checker @if (!isDisabled && flatCheckerPerms.length > 0) { - - } @@ -239,14 +330,18 @@
} -
@if (!isDisabled) {

{{ 'labels.heading.Manage Roles and Permissions' | translate }}

-

{{ 'labels.text.Define or modify roles and associated permissions' | translate }}

+

+ {{ 'labels.text.Define or modify roles and associated permissions' | translate }} +

@@ -147,7 +149,9 @@

{{ 'labels.heading.Account Number Preferences' | translate }}

-

{{ 'labels.text.Preferences for generating account numbers for client' | translate }}

+

+ {{ 'labels.text.Preferences for generating account numbers for client' | translate }} +

@@ -207,7 +211,9 @@

{{ 'labels.heading.Manage External Events' | translate }}

-

{{ 'labels.text.External Events configuration, to enable or disable' | translate }}

+

+ {{ 'labels.text.External Events configuration, to enable or disable' | translate }} +

@@ -219,7 +225,9 @@

{{ 'labels.heading.System Information' | translate }}

-

{{ 'labels.text.View system version, server and licensing information' | translate }}

+

+ {{ 'labels.text.View system version, server and licensing information' | translate }} +

@@ -231,9 +239,15 @@

{{ 'labels.heading.Manage Data Tables' | translate }}

{{ 'labels.text.Mifos X data-tables allow the user to add custom fields' | translate }}

- - - + + +
@@ -241,9 +255,15 @@

{{ 'labels.heading.Manage Codes' | translate }}

{{ 'labels.text.Selection you can create new drop-downs and edit' | translate }}

- - - + + +
@@ -251,9 +271,15 @@

{{ 'labels.heading.Manage Roles and Permissions' | translate }}

{{ 'labels.text.Roles and Permissions allow the administrator to control access' | translate }}

- - - + + +
@@ -261,9 +287,15 @@

{{ 'labels.heading.Configure Maker-Checker Tasks' | translate }}

{{ 'labels.text."Maker-Checker" principle requires every tasks' | translate }}

- - - + + +
@@ -271,9 +303,15 @@

{{ 'labels.heading.Configuration' | translate }}

{{ 'labels.text.Configurations page allows you to further customize' | translate }}

- - - + + +
@@ -281,9 +319,15 @@

{{ 'labels.heading.Manage Scheduler Jobs' | translate }}

{{ 'labels.text.Scheduler Jobs can set batch jobs' | translate }}

- - - + + +
@@ -291,8 +335,14 @@

{{ 'labels.heading.Manage Reports' | translate }}

{{ 'labels.text.Add customized reports and edit core reports' | translate }}

- - - + + +
diff --git a/src/app/system/system.component.scss b/src/app/system/system.component.scss index ca9bfdf30..0fbc1822e 100644 --- a/src/app/system/system.component.scss +++ b/src/app/system/system.component.scss @@ -61,7 +61,7 @@ $gray-900: #0f172a; grid-template-columns: repeat(2, 1fr); gap: 12px; - @media (max-width: 768px) { + @media (width <= 768px) { grid-template-columns: 1fr; } } @@ -82,7 +82,7 @@ $gray-900: #0f172a; &:hover { border-color: #cbd5e1; background: $gray-50; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + box-shadow: 0 2px 8px rgb(0 0 0 / 4%); .sys-card__arrow { color: $gray-700; @@ -105,17 +105,60 @@ $gray-900: #0f172a; font-size: 15px; flex-shrink: 0; - &--blue { background: rgba(59, 130, 246, 0.1); color: #2563eb; } - &--amber { background: rgba(245, 158, 11, 0.1); color: #d97706; } - &--green { background: rgba(34, 197, 94, 0.1); color: #16a34a; } - &--emerald { background: rgba(16, 185, 129, 0.1); color: #059669; } - &--purple { background: rgba(139, 92, 246, 0.1); color: #7c3aed; } - &--indigo { background: rgba(99, 102, 241, 0.1); color: #4f46e5; } - &--slate { background: rgba(100, 116, 139, 0.1); color: #475569; } - &--orange { background: rgba(249, 115, 22, 0.1); color: #ea580c; } - &--teal { background: rgba(20, 184, 166, 0.1); color: #0d9488; } - &--cyan { background: rgba(6, 182, 212, 0.1); color: #0891b2; } - &--rose { background: rgba(244, 63, 94, 0.1); color: #e11d48; } + &--blue { + background: rgb(59 130 246 / 10%); + color: #2563eb; + } + + &--amber { + background: rgb(245 158 11 / 10%); + color: #d97706; + } + + &--green { + background: rgb(34 197 94 / 10%); + color: #16a34a; + } + + &--emerald { + background: rgb(16 185 129 / 10%); + color: #059669; + } + + &--purple { + background: rgb(139 92 246 / 10%); + color: #7c3aed; + } + + &--indigo { + background: rgb(99 102 241 / 10%); + color: #4f46e5; + } + + &--slate { + background: rgb(100 116 139 / 10%); + color: #475569; + } + + &--orange { + background: rgb(249 115 22 / 10%); + color: #ea580c; + } + + &--teal { + background: rgb(20 184 166 / 10%); + color: #0d9488; + } + + &--cyan { + background: rgb(6 182 212 / 10%); + color: #0891b2; + } + + &--rose { + background: rgb(244 63 94 / 10%); + color: #e11d48; + } } &__body { @@ -152,8 +195,13 @@ $gray-900: #0f172a; // ── Dark mode ── :host-context(.dark-theme) { .sys-header { - &__title { color: #e2e8f0; } - &__sub { color: #94a3b8; } + &__title { + color: #e2e8f0; + } + + &__sub { + color: #94a3b8; + } } .sys-card { @@ -165,8 +213,16 @@ $gray-900: #0f172a; border-color: #444; } - &__title { color: #e2e8f0; } - &__desc { color: #64748b; } - &__arrow { color: #444; } + &__title { + color: #e2e8f0; + } + + &__desc { + color: #64748b; + } + + &__arrow { + color: #444; + } } } diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/checker-inbox/checker-inbox.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/checker-inbox/checker-inbox.component.scss index 03dce57e1..c230c65ba 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/checker-inbox/checker-inbox.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/checker-inbox/checker-inbox.component.scss @@ -32,7 +32,9 @@ $text-muted: #64748b; max-width: 320px; ::ng-deep { - .mat-mdc-form-field-flex { height: 36px; } + .mat-mdc-form-field-flex { + height: 36px; + } .mat-mdc-form-field-infix { padding: 6px 0; @@ -45,8 +47,13 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } .mat-mdc-form-field-icon-prefix { @@ -55,7 +62,9 @@ $text-muted: #64748b; font-size: 16px; } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -68,7 +77,9 @@ $text-muted: #64748b; color: $text-muted; background: #fff; - ::ng-deep .mat-mdc-button-persistent-ripple { border-radius: 8px; } + ::ng-deep .mat-mdc-button-persistent-ripple { + border-radius: 8px; + } } .cv-action-buttons { @@ -84,7 +95,7 @@ $text-muted: #64748b; border-radius: 8px !important; height: 36px; padding: 0 16px; - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; fa-icon { margin-right: 4px; @@ -153,12 +164,19 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } input, - .mat-mdc-select-trigger { font-size: 13px; } + .mat-mdc-select-trigger { + font-size: 13px; + } } } @@ -169,7 +187,7 @@ $text-muted: #64748b; } // Responsive -@media (max-width: 768px) { +@media (width <= 768px) { .cv-action-bar { flex-direction: column; align-items: stretch; @@ -183,7 +201,9 @@ $text-muted: #64748b; margin-left: 0; justify-content: stretch; - .cv-btn { flex: 1; } + .cv-btn { + flex: 1; + } } .cv-advanced-search { diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.html b/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.html index 431c1eedf..7c79d01ee 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.html +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.html @@ -62,18 +62,34 @@
- - - +
{{ 'labels.inputs.Name' | translate }} @@ -130,11 +126,7 @@ @if ((chargesDataSource | chargesPenaltyFilter: true).length > 0) {
- +
- + [queryParams]="{ type: row.reportType, id: row.id }" + >
{{ 'labels.inputs.Name' | translate }} diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.scss index a9cfa9a1f..d40566222 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.scss @@ -19,7 +19,9 @@ $gray-900: #202124; overflow: hidden; background: #fff; - & + & { margin-top: 16px; } + & + & { + margin-top: 16px; + } &__header { display: flex; @@ -29,8 +31,17 @@ $gray-900: #202124; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } @@ -59,7 +70,11 @@ $gray-900: #202124; @media (width <= 600px) { flex-direction: column; - &__select { max-width: 100%; width: 100%; } + + &__select { + max-width: 100%; + width: 100%; + } } } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.scss index 5dbbf06b8..6f7269cbe 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.scss @@ -18,7 +18,9 @@ $gray-900: #202124; overflow: hidden; background: #fff; - & + & { margin-top: 16px; } + & + & { + margin-top: 16px; + } &__header { display: flex; @@ -28,8 +30,17 @@ $gray-900: #202124; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } @@ -39,12 +50,17 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .lpc-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } .lpc-checkbox-row { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss index 01e69962d..e0ff5016a 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.scss @@ -18,7 +18,9 @@ $gray-900: #202124; overflow: hidden; background: #fff; - & + & { margin-top: 16px; } + & + & { + margin-top: 16px; + } &__header { display: flex; @@ -28,8 +30,17 @@ $gray-900: #202124; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } @@ -39,12 +50,17 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .lpd-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } .lpd-checkbox-row { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component.scss index c8dc9d8b9..0ab0abedd 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-interest-refund-step/loan-product-interest-refund-step.component.scss @@ -26,8 +26,17 @@ $gray-900: #202124; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } @@ -37,7 +46,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .lpir-field { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html index 1699b2a69..78499926d 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html @@ -7,12 +7,14 @@ -->
-
-

{{ 'labels.inputs.products.loan.Amortization' | translate }} & {{ 'labels.inputs.Interest Method' | translate }}

+

+ {{ 'labels.inputs.products.loan.Amortization' | translate }} & + {{ 'labels.inputs.Interest Method' | translate }} +

@@ -60,7 +62,10 @@ matTooltip="{{ 'tooltips.Daily - Will Calculate the interest' | translate }}" required > - @for (interestCalculationPeriodType of interestCalculationPeriodTypeData; track interestCalculationPeriodType) { + @for ( + interestCalculationPeriodType of interestCalculationPeriodTypeData; + track interestCalculationPeriodType + ) { {{ interestCalculationPeriodType.value | translateKey: 'catalogs' }} @@ -118,7 +123,10 @@ required [disabled]="advancedTransactionProcessingStrategyDisabled" > - @for (transactionProcessingStrategy of transactionProcessingStrategyData; track transactionProcessingStrategy) { + @for ( + transactionProcessingStrategy of transactionProcessingStrategyData; + track transactionProcessingStrategy + ) { {{ transactionProcessingStrategy.name | translateKey: 'catalogs' }} @@ -374,7 +382,10 @@

{{ 'labels.inputs.Moratorium' | translate }} - +

@@ -418,7 +429,9 @@
-

{{ 'labels.inputs.Number of days a loan may be overdue before moving into arrears' | translate }}

+

+ {{ 'labels.inputs.Number of days a loan may be overdue before moving into arrears' | translate }} +

@@ -432,10 +445,7 @@
- + {{ 'labels.inputs.Account moves out of NPA only after all arrears have been cleared' | translate }}
@@ -729,20 +739,14 @@ }
- + {{ 'labels.inputs.Is Arrears recognization based on original schedule' | translate }}?
@if (loanProductSettingsForm.value.loanScheduleType === 'PROGRESSIVE') {
- + {{ 'labels.inputs.Do not calculate interest on past due principal balances' | translate }}
@@ -791,7 +795,10 @@

{{ 'labels.heading.Event Settings' | translate }} - +

@@ -889,5 +896,4 @@ }
- diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html index adc71db05..1912b6b49 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html @@ -7,7 +7,6 @@ -->
-
@@ -495,7 +494,11 @@ mat-icon-button color="primary" (click)=" - editVariationsForBorrowerCycle('NumberOfRepayments', numberOfRepaymentVariationsForBorrowerCycle, i) + editVariationsForBorrowerCycle( + 'NumberOfRepayments', + numberOfRepaymentVariationsForBorrowerCycle, + i + ) " > @@ -655,5 +658,4 @@
-
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html index 45a1cd4d5..2c4f8daf1 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html @@ -49,7 +49,12 @@
- +
{{ 'labels.inputs.Classification' | translate }} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss index 4ba22656a..ad249f760 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .db-field { @@ -113,7 +120,10 @@ $gray-900: #202124; padding: 0 14px !important; box-shadow: 0 1px 3px rgba($primary, 0.25) !important; - fa-icon { margin-right: 6px; font-size: 11px; } + fa-icon { + margin-right: 6px; + font-size: 11px; + } } // Ranges table @@ -149,7 +159,9 @@ $gray-900: #202124; height: 40px; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } } .mat-mdc-cell { @@ -208,6 +220,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html index 6b882d129..88c4f6073 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html @@ -49,7 +49,12 @@
- +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss index 4ba22656a..ad249f760 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .db-field { @@ -113,7 +120,10 @@ $gray-900: #202124; padding: 0 14px !important; box-shadow: 0 1px 3px rgba($primary, 0.25) !important; - fa-icon { margin-right: 6px; font-size: 11px; } + fa-icon { + margin-right: 6px; + font-size: 11px; + } } // Ranges table @@ -149,7 +159,9 @@ $gray-900: #202124; height: 40px; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } } .mat-mdc-cell { @@ -208,6 +220,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html index 52bda2454..d2a2d575c 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html @@ -16,7 +16,13 @@

{{ delinquencyBucketData.name }}

- @@ -59,7 +65,9 @@ @if (delinquencyBucketData.ranges && delinquencyBucketData.ranges.length > 0) {
- {{ 'labels.inputs.Classification' | translate }} {{ 'labels.inputs.Name' | translate }} + {{ 'labels.inputs.Classification' | translate }} {{ 'labels.inputs.Name' | translate }} {{ 'labels.inputs.Days From' | translate }} {{ 'labels.inputs.Days Till' | translate }}
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss index 76f8a511d..16429e7af 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss @@ -72,9 +72,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -127,7 +132,9 @@ $gray-900: #202124; padding: 10px 0; border-bottom: 1px solid #f1f3f4; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } &__label { flex: 0 0 40%; @@ -170,9 +177,13 @@ $gray-900: #202124; border-bottom: 1px solid #f1f5f9; transition: background 0.1s; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } - &:hover { background: #f8fafc; } + &:hover { + background: #f8fafc; + } .vb-ranges-cell { font-size: 13px; diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss index c191f95f5..48c17f275 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss @@ -6,34 +6,76 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.cr-container { max-width: 960px; margin: 0 auto; padding: 24px 16px; } +.cr-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; +} .cr-header { - display: flex; align-items: center; gap: 10px; margin-bottom: 20px; - &__icon { font-size: 20px; color: #1353a0; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; + + &__icon { + font-size: 20px; + color: #1353a0; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; + } } .cr-section { - border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; background: #fff; + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + background: #fff; &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: #f8f9fa; border-bottom: 1px solid #e8eaed; - i { font-size: 14px; color: #0ea5e9; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: #202124; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: #f8f9fa; + border-bottom: 1px solid #e8eaed; + + i { + font-size: 14px; + color: #0ea5e9; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: #202124; + } } } .cr-grid { - display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 16px; padding: 16px 24px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px 16px; + padding: 16px 24px; } .cr-action-bar { - display: flex; justify-content: flex-end; gap: 12px; margin-top: 20px; padding: 16px 0; + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 20px; + padding: 16px 0; border-top: 1px solid rgb(0 0 0 / 8%); } @media (width <= 600px) { - .cr-grid { grid-template-columns: 1fr; } + .cr-grid { + grid-template-columns: 1fr; + } } diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html index e4a99d247..4e56b3d84 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html @@ -23,7 +23,13 @@
- diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html index e8f1fa05d..cdb5aa732 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html @@ -9,7 +9,9 @@
-

{{ 'labels.buttons.Edit' | translate }} {{ 'labels.inputs.Delinquency Range' | translate }}

+

+ {{ 'labels.buttons.Edit' | translate }} {{ 'labels.inputs.Delinquency Range' | translate }} +

diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss index 53d95280c..cd48679b9 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss @@ -6,34 +6,76 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.er-container { max-width: 960px; margin: 0 auto; padding: 24px 16px; } +.er-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; +} .er-header { - display: flex; align-items: center; gap: 10px; margin-bottom: 20px; - &__icon { font-size: 20px; color: #1353a0; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; + + &__icon { + font-size: 20px; + color: #1353a0; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; + } } .er-section { - border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; background: #fff; + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + background: #fff; &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: #f8f9fa; border-bottom: 1px solid #e8eaed; - i { font-size: 14px; color: #0ea5e9; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: #202124; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: #f8f9fa; + border-bottom: 1px solid #e8eaed; + + i { + font-size: 14px; + color: #0ea5e9; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: #202124; + } } } .er-grid { - display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 16px; padding: 16px 24px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px 16px; + padding: 16px 24px; } .er-action-bar { - display: flex; justify-content: flex-end; gap: 12px; margin-top: 20px; padding: 16px 0; + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 20px; + padding: 16px 0; border-top: 1px solid rgb(0 0 0 / 8%); } @media (width <= 600px) { - .er-grid { grid-template-columns: 1fr; } + .er-grid { + grid-template-columns: 1fr; + } } diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html index b5604a173..7bf7dad91 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html @@ -13,11 +13,21 @@

{{ delinquencyRangeData.classification }}

- - diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss index c2b9146e6..c6fe89e2f 100644 --- a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss @@ -6,38 +6,94 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.vr-container { max-width: 960px; margin: 0 auto; padding: 24px 16px; } +.vr-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; +} .vr-top-bar { - display: flex; align-items: center; justify-content: space-between; - flex-wrap: wrap; gap: 12px; margin-bottom: 20px; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 12px; + margin-bottom: 20px; - &__actions { display: flex; gap: 12px; } + &__actions { + display: flex; + gap: 12px; + } } .vr-header { - display: flex; align-items: center; gap: 10px; - &__icon { font-size: 20px; color: #1353a0; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } + display: flex; + align-items: center; + gap: 10px; + + &__icon { + font-size: 20px; + color: #1353a0; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; + } } .vr-section { - border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; background: #fff; + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + background: #fff; &__header { - display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: #f8f9fa; border-bottom: 1px solid #e8eaed; - i { font-size: 14px; color: #0ea5e9; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: #202124; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: #f8f9fa; + border-bottom: 1px solid #e8eaed; + + i { + font-size: 14px; + color: #0ea5e9; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: #202124; + } } - &__body { padding: 16px 24px; } + &__body { + padding: 16px 24px; + } } .vr-row { - display: flex; padding: 6px 0; border-bottom: 1px solid #f1f3f4; - &:last-child { border-bottom: none; } + display: flex; + padding: 6px 0; + border-bottom: 1px solid #f1f3f4; + + &:last-child { + border-bottom: none; + } } -.vr-label { flex: 0 0 40%; font-size: 13px; font-weight: 500; color: #5f6368; } -.vr-value { flex: 1; font-size: 13px; color: #202124; } +.vr-label { + flex: 0 0 40%; + font-size: 13px; + font-weight: 500; + color: #5f6368; +} + +.vr-value { + flex: 1; + font-size: 13px; + color: #202124; +} diff --git a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss index cc835be50..4398e1cca 100644 --- a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss +++ b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss @@ -6,20 +6,48 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -.mdb-container { max-width: 960px; margin: 0 auto; padding: 24px 16px; } - -.mdb-header { - display: flex; align-items: center; gap: 10px; margin-bottom: 20px; - &__icon { font-size: 20px; color: #1353a0; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } +.mdb-container { + max-width: 960px; + margin: 0 auto; + padding: 24px 16px; } -.mdb-cards { display: flex; flex-direction: column; gap: 12px; } +.mdb-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 20px; + + &__icon { + font-size: 20px; + color: #1353a0; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; + } +} + +.mdb-cards { + display: flex; + flex-direction: column; + gap: 12px; +} .mdb-card { - display: flex; align-items: center; gap: 16px; padding: 16px 24px; - border: 1px solid #e2e8f0; border-radius: 10px; background: #fff; - cursor: pointer; transition: box-shadow 0.2s, border-color 0.2s; + display: flex; + align-items: center; + gap: 16px; + padding: 16px 24px; + border: 1px solid #e2e8f0; + border-radius: 10px; + background: #fff; + cursor: pointer; + transition: + box-shadow 0.2s, + border-color 0.2s; &:hover { border-color: #0ea5e9; @@ -27,16 +55,36 @@ } &__icon-wrap { - display: flex; align-items: center; justify-content: center; - width: 40px; height: 40px; border-radius: 8px; - background: #f0f9ff; color: #0ea5e9; font-size: 16px; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 8px; + background: #f0f9ff; + color: #0ea5e9; + font-size: 16px; } &__content { flex: 1; - h3 { margin: 0; font-size: 14px; font-weight: 600; color: #202124; } - p { margin: 4px 0 0; font-size: 12px; color: #5f6368; } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: #202124; + } + + p { + margin: 4px 0 0; + font-size: 12px; + color: #5f6368; + } } - &__arrow { font-size: 12px; color: #9ca3af; } + &__arrow { + font-size: 12px; + color: #9ca3af; + } } diff --git a/src/app/products/manage-tax-components/create-tax-component/create-tax-component.component.scss b/src/app/products/manage-tax-components/create-tax-component/create-tax-component.component.scss index ddf54f2c6..b2ce5d857 100644 --- a/src/app/products/manage-tax-components/create-tax-component/create-tax-component.component.scss +++ b/src/app/products/manage-tax-components/create-tax-component/create-tax-component.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ctc-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-tax-components/edit-tax-component/edit-tax-component.component.scss b/src/app/products/manage-tax-components/edit-tax-component/edit-tax-component.component.scss index 4eddbd8f5..880003338 100644 --- a/src/app/products/manage-tax-components/edit-tax-component/edit-tax-component.component.scss +++ b/src/app/products/manage-tax-components/edit-tax-component/edit-tax-component.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .etc-field { @@ -128,6 +135,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-tax-components/manage-tax-components.component.html b/src/app/products/manage-tax-components/manage-tax-components.component.html index 8e9a15b80..c1f553d12 100644 --- a/src/app/products/manage-tax-components/manage-tax-components.component.html +++ b/src/app/products/manage-tax-components/manage-tax-components.component.html @@ -23,7 +23,13 @@
- diff --git a/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.html b/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.html index 89518fea8..7efef27a5 100644 --- a/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.html +++ b/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.html @@ -15,7 +15,13 @@

{{ taxComponentData.name }}

- diff --git a/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.scss b/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.scss index 5d3396259..9449cdf72 100644 --- a/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.scss +++ b/src/app/products/manage-tax-components/view-tax-component/view-tax-component.component.scss @@ -60,9 +60,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -81,7 +86,9 @@ $gray-900: #202124; padding: 20px 28px 24px; background: #fff; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vtc-cell { diff --git a/src/app/products/manage-tax-configurations/manage-tax-configurations.component.html b/src/app/products/manage-tax-configurations/manage-tax-configurations.component.html index 3a8aa5ec6..b7c1718d7 100644 --- a/src/app/products/manage-tax-configurations/manage-tax-configurations.component.html +++ b/src/app/products/manage-tax-configurations/manage-tax-configurations.component.html @@ -14,7 +14,10 @@

{{ 'labels.heading.Manage Tax Configurations' | translate }}

-

{{ 'labels.heading.Define Tax Components' | translate }} & {{ 'labels.heading.Define Tax Groups' | translate }}

+

+ {{ 'labels.heading.Define Tax Components' | translate }} & + {{ 'labels.heading.Define Tax Groups' | translate }} +

diff --git a/src/app/products/manage-tax-configurations/manage-tax-configurations.component.scss b/src/app/products/manage-tax-configurations/manage-tax-configurations.component.scss index b107dad99..d3e9a2e05 100644 --- a/src/app/products/manage-tax-configurations/manage-tax-configurations.component.scss +++ b/src/app/products/manage-tax-configurations/manage-tax-configurations.component.scss @@ -78,7 +78,10 @@ $gray-900: #0f172a; border-radius: 10px; text-decoration: none; cursor: pointer; - transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s; + transition: + border-color 0.15s, + box-shadow 0.15s, + transform 0.12s; &:hover { border-color: #cbd5e1; diff --git a/src/app/products/manage-tax-groups/create-tax-group/create-tax-group.component.scss b/src/app/products/manage-tax-groups/create-tax-group/create-tax-group.component.scss index 38a8f6a0a..0711862b6 100644 --- a/src/app/products/manage-tax-groups/create-tax-group/create-tax-group.component.scss +++ b/src/app/products/manage-tax-groups/create-tax-group/create-tax-group.component.scss @@ -45,9 +45,14 @@ $text-muted: #64748b; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -102,7 +107,9 @@ $text-muted: #64748b; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .ctg-field { @@ -143,7 +150,9 @@ $text-muted: #64748b; height: 36px; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } } .mat-mdc-cell { @@ -192,6 +201,9 @@ $text-muted: #64748b; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-tax-groups/edit-tax-group/edit-tax-group.component.scss b/src/app/products/manage-tax-groups/edit-tax-group/edit-tax-group.component.scss index 5558ddbaa..e436eb3d5 100644 --- a/src/app/products/manage-tax-groups/edit-tax-group/edit-tax-group.component.scss +++ b/src/app/products/manage-tax-groups/edit-tax-group/edit-tax-group.component.scss @@ -45,9 +45,14 @@ $text-muted: #64748b; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -102,7 +107,9 @@ $text-muted: #64748b; gap: 4px 16px; padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .etg-field { @@ -143,7 +150,9 @@ $text-muted: #64748b; height: 36px; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } } .mat-mdc-cell { @@ -192,6 +201,9 @@ $text-muted: #64748b; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/products/manage-tax-groups/manage-tax-groups.component.html b/src/app/products/manage-tax-groups/manage-tax-groups.component.html index 43e311431..3a0172e19 100644 --- a/src/app/products/manage-tax-groups/manage-tax-groups.component.html +++ b/src/app/products/manage-tax-groups/manage-tax-groups.component.html @@ -23,7 +23,13 @@
- diff --git a/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.html b/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.html index d954b17d7..b32dbd180 100644 --- a/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.html +++ b/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.html @@ -15,7 +15,13 @@

{{ taxGroupData.name }}

- diff --git a/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.scss b/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.scss index 107152abd..b947bd1ff 100644 --- a/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.scss +++ b/src/app/products/manage-tax-groups/view-tax-group/view-tax-group.component.scss @@ -61,9 +61,14 @@ $text-muted: #64748b; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -109,7 +114,9 @@ $text-muted: #64748b; gap: 1px; padding: 20px 28px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .vtg-cell { diff --git a/src/app/products/products-mix/products-mix.component.html b/src/app/products/products-mix/products-mix.component.html index 18acae84b..725c8ceb0 100644 --- a/src/app/products/products-mix/products-mix.component.html +++ b/src/app/products/products-mix/products-mix.component.html @@ -24,8 +24,14 @@
-
@@ -40,12 +46,7 @@ - +
{{ 'labels.inputs.Classification' | translate }} {{ row.classification }}
diff --git a/src/app/products/products-mix/view-product-mix/view-product-mix.component.html b/src/app/products/products-mix/view-product-mix/view-product-mix.component.html index f529dacbe..bdff6e740 100644 --- a/src/app/products/products-mix/view-product-mix/view-product-mix.component.html +++ b/src/app/products/products-mix/view-product-mix/view-product-mix.component.html @@ -14,11 +14,11 @@
diff --git a/src/app/products/products.component.html b/src/app/products/products.component.html index f61436ca1..b68ad5f2f 100644 --- a/src/app/products/products.component.html +++ b/src/app/products/products.component.html @@ -25,7 +25,9 @@

{{ 'labels.heading.Loan Products' | translate }}

-

{{ 'labels.text.Add new loan product or modify or inactivate loan product' | translate }}

+

+ {{ 'labels.text.Add new loan product or modify or inactivate loan product' | translate }} +

@@ -38,7 +40,9 @@

{{ 'labels.heading.Savings Products' | translate }}

-

{{ 'labels.text.Add new savings product or modify or inactivate savings product' | translate }}

+

+ {{ 'labels.text.Add new savings product or modify or inactivate savings product' | translate }} +

@@ -62,7 +66,9 @@

{{ 'labels.heading.Share Products' | translate }}

-

{{ 'labels.text.Add new share product or modify or inactivate share product' | translate }}

+

+ {{ 'labels.text.Add new share product or modify or inactivate share product' | translate }} +

@@ -82,13 +88,19 @@

{{ 'labels.heading.Charges' | translate }}

-

{{ 'labels.text.Define charges/penalties for loan products, savings and deposit products' | translate }}

+

+ {{ 'labels.text.Define charges/penalties for loan products, savings and deposit products' | translate }} +

@@ -129,13 +143,19 @@ - +

{{ 'labels.heading.Delinquency Buckets' | translate }}

-

{{ 'labels.text.Define delinquency day ranges and bucket set for loan products' | translate }}

+

+ {{ 'labels.text.Define delinquency day ranges and bucket set for loan products' | translate }} +

diff --git a/src/app/products/products.component.scss b/src/app/products/products.component.scss index 698497b5f..b7997935e 100644 --- a/src/app/products/products.component.scss +++ b/src/app/products/products.component.scss @@ -195,8 +195,13 @@ $gray-900: #0f172a; // ── Dark mode ── :host-context(.dark-theme) { .pd-header { - &__title { color: #e2e8f0; } - &__sub { color: #94a3b8; } + &__title { + color: #e2e8f0; + } + + &__sub { + color: #94a3b8; + } } .pd-card { @@ -208,8 +213,16 @@ $gray-900: #0f172a; border-color: #444; } - &__title { color: #e2e8f0; } - &__desc { color: #64748b; } - &__arrow { color: #444; } + &__title { + color: #e2e8f0; + } + + &__desc { + color: #64748b; + } + + &__arrow { + color: #444; + } } } diff --git a/src/app/products/share-products/create-share-product/create-share-product.component.html b/src/app/products/share-products/create-share-product/create-share-product.component.html index 943f2344f..89efc79c2 100644 --- a/src/app/products/share-products/create-share-product/create-share-product.component.html +++ b/src/app/products/share-products/create-share-product/create-share-product.component.html @@ -12,7 +12,12 @@

{{ 'labels.heading.Create Share Product' | translate }}

- + @@ -120,19 +125,19 @@
@if (selectedTabIndex > 0) { } @if (selectedTabIndex < totalTabs - 1) { } @if (shareProductFormValid && selectedTabIndex === totalTabs - 1) { } diff --git a/src/app/products/share-products/create-share-product/create-share-product.component.scss b/src/app/products/share-products/create-share-product/create-share-product.component.scss index 6f7b6c969..9b7a48862 100644 --- a/src/app/products/share-products/create-share-product/create-share-product.component.scss +++ b/src/app/products/share-products/create-share-product/create-share-product.component.scss @@ -20,8 +20,16 @@ $primary: #1353a0; gap: 10px; margin-bottom: 20px; - &__icon { font-size: 20px; color: $primary; } - &__title { margin: 0; font-size: 22px; font-weight: 500; } + &__icon { + font-size: 20px; + color: $primary; + } + + &__title { + margin: 0; + font-size: 22px; + font-weight: 500; + } } .shp-tabs { @@ -30,16 +38,38 @@ $primary: #1353a0; box-shadow: 0 2px 8px rgb(0 0 0 / 8%); ::ng-deep { - .mat-mdc-tab-header { border-bottom: 1px solid rgb(0 0 0 / 8%); } - .mat-mdc-tab { min-width: 100px; padding: 0 12px; height: 52px; } - .mat-mdc-tab-body-wrapper { min-height: 200px; } - .mat-mdc-tab-body { overflow: visible !important; } - .mat-mdc-tab-body-content { overflow: visible !important; } + .mat-mdc-tab-header { + border-bottom: 1px solid rgb(0 0 0 / 8%); + } + + .mat-mdc-tab { + min-width: 100px; + padding: 0 12px; + height: 52px; + } + + .mat-mdc-tab-body-wrapper { + min-height: 200px; + } + + .mat-mdc-tab-body { + overflow: visible !important; + } + + .mat-mdc-tab-body-content { + overflow: visible !important; + } } } -.shp-tab-icon { margin-right: 8px; font-size: 14px; } -.shp-tab-content { padding: 24px 16px; } +.shp-tab-icon { + margin-right: 8px; + font-size: 14px; +} + +.shp-tab-content { + padding: 24px 16px; +} .shp-action-bar { display: flex; @@ -49,6 +79,12 @@ $primary: #1353a0; padding: 16px 0; border-top: 1px solid rgb(0 0 0 / 8%); - &__cancel { color: rgb(0 0 0 / 60%); } - &__right { display: flex; gap: 12px; } + &__cancel { + color: rgb(0 0 0 / 60%); + } + + &__right { + display: flex; + gap: 12px; + } } diff --git a/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.html b/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.html index 75e830b9f..26e818619 100644 --- a/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.html +++ b/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.html @@ -25,7 +25,14 @@ - diff --git a/src/app/products/share-products/share-product-stepper/share-product-currency-step/share-product-currency-step.component.scss b/src/app/products/share-products/share-product-stepper/share-product-currency-step/share-product-currency-step.component.scss index 2b4edfb6d..3b5dd9b20 100644 --- a/src/app/products/share-products/share-product-stepper/share-product-currency-step/share-product-currency-step.component.scss +++ b/src/app/products/share-products/share-product-stepper/share-product-currency-step/share-product-currency-step.component.scss @@ -18,28 +18,53 @@ $gray-900: #202124; overflow: hidden; background: #fff; - & + & { margin-top: 16px; } + & + & { + margin-top: 16px; + } &__header { - display: flex; align-items: center; gap: 10px; - padding: 12px 24px; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } .shpcr-grid { - display: grid; grid-template-columns: repeat(2, 1fr); - gap: 4px 16px; padding: 16px 24px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px 16px; + padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .shpcr-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } .shpcr-checkbox-row { - display: flex; align-items: center; padding: 4px 0; + display: flex; + align-items: center; + padding: 4px 0; } diff --git a/src/app/products/share-products/share-product-stepper/share-product-details-step/share-product-details-step.component.scss b/src/app/products/share-products/share-product-stepper/share-product-details-step/share-product-details-step.component.scss index a8e05a519..96c75e6f1 100644 --- a/src/app/products/share-products/share-product-stepper/share-product-details-step/share-product-details-step.component.scss +++ b/src/app/products/share-products/share-product-stepper/share-product-details-step/share-product-details-step.component.scss @@ -19,21 +19,42 @@ $gray-900: #202124; background: #fff; &__header { - display: flex; align-items: center; gap: 10px; - padding: 12px 24px; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } .shpd-grid { - display: grid; grid-template-columns: repeat(2, 1fr); - gap: 4px 16px; padding: 16px 24px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px 16px; + padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .shpd-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } diff --git a/src/app/products/share-products/share-product-stepper/share-product-terms-step/share-product-terms-step.component.scss b/src/app/products/share-products/share-product-stepper/share-product-terms-step/share-product-terms-step.component.scss index 34b0f20d8..a9a1d2221 100644 --- a/src/app/products/share-products/share-product-stepper/share-product-terms-step/share-product-terms-step.component.scss +++ b/src/app/products/share-products/share-product-stepper/share-product-terms-step/share-product-terms-step.component.scss @@ -19,21 +19,42 @@ $gray-900: #202124; background: #fff; &__header { - display: flex; align-items: center; gap: 10px; - padding: 12px 24px; background: $gray-50; border-bottom: 1px solid $gray-200; - i { font-size: 14px; color: $accent; } - h3 { margin: 0; font-size: 14px; font-weight: 600; color: $gray-900; } + display: flex; + align-items: center; + gap: 10px; + padding: 12px 24px; + background: $gray-50; + border-bottom: 1px solid $gray-200; + + i { + font-size: 14px; + color: $accent; + } + + h3 { + margin: 0; + font-size: 14px; + font-weight: 600; + color: $gray-900; + } } } .shpt-grid { - display: grid; grid-template-columns: repeat(2, 1fr); - gap: 4px 16px; padding: 16px 24px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px 16px; + padding: 16px 24px; - @media (width <= 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .shpt-field { width: 100%; - &--full { grid-column: 1 / -1; } + + &--full { + grid-column: 1 / -1; + } } diff --git a/src/app/rbi-circulars/rbi-circulars.component.html b/src/app/rbi-circulars/rbi-circulars.component.html index cb4d20500..b75da74d0 100644 --- a/src/app/rbi-circulars/rbi-circulars.component.html +++ b/src/app/rbi-circulars/rbi-circulars.component.html @@ -203,11 +203,7 @@
- +
@@ -255,7 +251,11 @@
Search work items... - +
{{ wp.assignee }} - {{ formatOpDate(wp.dueDate) }} + {{ formatOpDate(wp.dueDate) }} {{ wp.project }}
@@ -289,17 +295,29 @@ {{ wp.percentageDone }}%
- -
-
Showing {{ opFilteredWorkPackages.length }} of {{ opWorkPackages.length }} work items
+
+ Showing {{ opFilteredWorkPackages.length }} of {{ opWorkPackages.length }} work items +
@@ -327,7 +345,11 @@
Priority - + {{ opSelectedWorkPackage.priority }}
@@ -354,7 +376,10 @@
Progress
- + {{ opSelectedWorkPackage.percentageDone }}%
diff --git a/src/app/rbi-circulars/rbi-circulars.component.scss b/src/app/rbi-circulars/rbi-circulars.component.scss index c38517fb5..2086f9871 100644 --- a/src/app/rbi-circulars/rbi-circulars.component.scss +++ b/src/app/rbi-circulars/rbi-circulars.component.scss @@ -423,7 +423,9 @@ border-radius: 10px; padding: 16px; cursor: pointer; - transition: box-shadow 0.2s, border-color 0.2s; + transition: + box-shadow 0.2s, + border-color 0.2s; &:hover { box-shadow: 0 4px 12px rgb(0 0 0 / 10%); diff --git a/src/app/rbi-circulars/rbi-circulars.component.ts b/src/app/rbi-circulars/rbi-circulars.component.ts index dec51c67e..10981e107 100644 --- a/src/app/rbi-circulars/rbi-circulars.component.ts +++ b/src/app/rbi-circulars/rbi-circulars.component.ts @@ -280,7 +280,10 @@ export class RbiCircularsComponent implements OnInit, AfterViewInit { } const q = query.toLowerCase(); this.opFilteredWorkPackages = this.opWorkPackages.filter( - (wp) => wp.subject?.toLowerCase().includes(q) || wp.status?.toLowerCase().includes(q) || wp.type?.toLowerCase().includes(q) + (wp) => + wp.subject?.toLowerCase().includes(q) || + wp.status?.toLowerCase().includes(q) || + wp.type?.toLowerCase().includes(q) ); } @@ -303,7 +306,9 @@ export class RbiCircularsComponent implements OnInit, AfterViewInit { formatOpDate(dateStr: string): string { if (!dateStr) return '—'; const d = new Date(dateStr); - return isNaN(d.getTime()) ? '—' : d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); + return isNaN(d.getTime()) + ? '—' + : d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); } private buildTabs() { diff --git a/src/app/reports/reports.component.html b/src/app/reports/reports.component.html index f63edec37..5025998dc 100644 --- a/src/app/reports/reports.component.html +++ b/src/app/reports/reports.component.html @@ -50,7 +50,11 @@
- @@ -65,7 +69,11 @@
- +
@@ -124,9 +132,13 @@
diff --git a/src/app/reports/reports.component.scss b/src/app/reports/reports.component.scss index 2f3d85fd3..686fd2d24 100644 --- a/src/app/reports/reports.component.scss +++ b/src/app/reports/reports.component.scss @@ -60,10 +60,21 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; span { border-radius: 2px; - &:nth-child(1) { background: $brand; } - &:nth-child(2) { background: $accent; } - &:nth-child(3) { background: #10b981; } - &:nth-child(4) { background: #cbd5e1; } + &:nth-child(1) { + background: $brand; + } + + &:nth-child(2) { + background: $accent; + } + + &:nth-child(3) { + background: #10b981; + } + + &:nth-child(4) { + background: #cbd5e1; + } } } @@ -83,21 +94,33 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; } ::ng-deep { - .mat-mdc-form-field-flex { height: 38px; } + .mat-mdc-form-field-flex { + height: 38px; + } .mat-mdc-form-field-infix { padding: 6px 0; min-height: 38px; } - .mat-mdc-form-field-subscript-wrapper { height: 0; min-height: 0; } - - .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mat-mdc-form-field-subscript-wrapper { + height: 0; + min-height: 0; } - input { font-size: 13px; } + .mdc-notched-outline { + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } + } + + input { + font-size: 13px; + } } } @@ -107,8 +130,13 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; grid-template-columns: repeat(3, 1fr); gap: 12px; - @media (max-width: 900px) { grid-template-columns: repeat(2, 1fr); } - @media (max-width: 500px) { grid-template-columns: 1fr; } + @media (width <= 900px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (width <= 500px) { + grid-template-columns: 1fr; + } } .rpt-tile { @@ -117,13 +145,15 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; gap: 14px; padding: 16px 18px; background: #fff; - border: 1px solid rgba(0, 0, 0, 0.06); + border: 1px solid rgb(0 0 0 / 6%); border-radius: 8px; cursor: pointer; - transition: box-shadow 0.15s, transform 0.15s; + transition: + box-shadow 0.15s, + transform 0.15s; &:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); + box-shadow: 0 4px 16px rgb(0 0 0 / 8%); transform: translateY(-1px); } @@ -137,12 +167,35 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; font-size: 16px; flex-shrink: 0; - &.blue { background: #e0f2fe; color: #0284c7; } - &.green { background: #d1fae5; color: #059669; } - &.purple { background: #ede9fe; color: #7c3aed; } - &.amber { background: #fef3c7; color: #d97706; } - &.teal { background: #ccfbf1; color: #0d9488; } - &.red { background: #fee2e2; color: #dc2626; } + &.blue { + background: #e0f2fe; + color: #0284c7; + } + + &.green { + background: #d1fae5; + color: #059669; + } + + &.purple { + background: #ede9fe; + color: #7c3aed; + } + + &.amber { + background: #fef3c7; + color: #d97706; + } + + &.teal { + background: #ccfbf1; + color: #0d9488; + } + + &.red { + background: #fee2e2; + color: #dc2626; + } } &__info { @@ -171,13 +224,17 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; transition: color 0.12s; } - &:hover &__arrow { color: $brand; } + &:hover &__arrow { + color: $brand; + } } .rpt-tiles--sub { grid-template-columns: repeat(2, 1fr); - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } // Detail view @@ -203,9 +260,14 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; border-color: $border !important; color: $text-muted !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $brand !important; border-color: $brand !important; } + &:hover { + color: $brand !important; + border-color: $brand !important; + } } // Table @@ -245,8 +307,13 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; height: 38px; border-bottom: 1px solid #f1f5f9; - &:nth-child(even) { background: #fafbfc; } - &:last-child { border-bottom: none; } + &:nth-child(even) { + background: #fafbfc; + } + + &:last-child { + border-bottom: none; + } } .mat-mdc-cell { @@ -294,11 +361,22 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; :host-context(.dark-theme) { .rpt-tile { background: #1e1e2e; - border-color: rgba(255, 255, 255, 0.06); + border-color: rgb(255 255 255 / 6%); } - .rpt-tile__name { color: #e0e0e0; } - .rpt-tile__arrow { color: #444; } - .rpt-header__title { color: #e0e0e0; } - .rpt-detail__title { color: #e0e0e0; } + .rpt-tile__name { + color: #e0e0e0; + } + + .rpt-tile__arrow { + color: #444; + } + + .rpt-header__title { + color: #e0e0e0; + } + + .rpt-detail__title { + color: #e0e0e0; + } } diff --git a/src/app/reports/reports.component.ts b/src/app/reports/reports.component.ts index 3fe1a64a3..fe22c0815 100644 --- a/src/app/reports/reports.component.ts +++ b/src/app/reports/reports.component.ts @@ -45,10 +45,36 @@ const DEFAULT_CONFIG = { icon: 'fa-chart-bar', color: 'blue' }; /** Sub-category rules for large categories (keyword → sub-category name) */ const SUB_CATEGORY_RULES: Record = { Loan: [ - { name: 'Disbursal & Approval', keywords: ['disbursal', 'pending approval', 'awaiting'], icon: 'fa-check-circle', color: 'green' }, - { name: 'Active & Portfolio', keywords: ['active loan', 'portfolio', 'outstanding', 'balance', 'summary'], icon: 'fa-chart-line', color: 'blue' }, - { name: 'Arrears & Aging', keywords: ['arrear', 'aging', 'overdue', 'delinquen', 'written off', 'write-off', 'obligation'], icon: 'fa-exclamation-triangle', color: 'red' }, - { name: 'Repayment & Collection', keywords: ['repayment', 'collection', 'demand', 'due', 'paid', 'payment', 'transaction'], icon: 'fa-money-bill', color: 'teal' }, + { name: 'Disbursal & Approval', keywords: [ + 'disbursal', + 'pending approval', + 'awaiting' + ], icon: 'fa-check-circle', color: 'green' }, + { name: 'Active & Portfolio', keywords: [ + 'active loan', + 'portfolio', + 'outstanding', + 'balance', + 'summary' + ], icon: 'fa-chart-line', color: 'blue' }, + { name: 'Arrears & Aging', keywords: [ + 'arrear', + 'aging', + 'overdue', + 'delinquen', + 'written off', + 'write-off', + 'obligation' + ], icon: 'fa-exclamation-triangle', color: 'red' }, + { name: 'Repayment & Collection', keywords: [ + 'repayment', + 'collection', + 'demand', + 'due', + 'paid', + 'payment', + 'transaction' + ], icon: 'fa-money-bill', color: 'teal' }, { name: 'Other Loan Reports', keywords: [], icon: 'fa-file-alt', color: 'purple' } ] }; @@ -89,7 +115,11 @@ export class ReportsComponent implements OnInit { reportsData: any[] = []; filter: string; - displayedColumns: string[] = ['reportName', 'reportType', 'reportCategory']; + displayedColumns: string[] = [ + 'reportName', + 'reportType', + 'reportCategory' + ]; dataSource = new MatTableDataSource(); /** Category dashboard */ @@ -136,10 +166,15 @@ export class ReportsComponent implements OnInit { } this.categories = Array.from(catMap.entries()) .sort((a, b) => b[1] - a[1]) - .map(([name, count]) => { - const cfg = CATEGORY_CONFIG[name] || DEFAULT_CONFIG; - return { name, count, icon: cfg.icon, color: cfg.color }; - }); + .map( + ([ + name, + count + ]) => { + const cfg = CATEGORY_CONFIG[name] || DEFAULT_CONFIG; + return { name, count, icon: cfg.icon, color: cfg.color }; + } + ); } selectCategory(category: string) { @@ -173,19 +208,31 @@ export class ReportsComponent implements OnInit { const matched = this.filteredReports.filter((r: any, idx: number) => { if (assigned.has(idx)) return false; const name = (r.reportName || '').toLowerCase(); - return rule.keywords.some(kw => name.includes(kw)); + return rule.keywords.some((kw) => name.includes(kw)); }); - matched.forEach(r => assigned.add(this.filteredReports.indexOf(r))); + matched.forEach((r) => assigned.add(this.filteredReports.indexOf(r))); if (matched.length > 0) { - this.subCategories.push({ name: rule.name, count: matched.length, icon: rule.icon, color: rule.color, reports: matched }); + this.subCategories.push({ + name: rule.name, + count: matched.length, + icon: rule.icon, + color: rule.color, + reports: matched + }); } } // "Other" bucket — remaining unmatched - const otherRule = rules.find(r => r.keywords.length === 0); + const otherRule = rules.find((r) => r.keywords.length === 0); const remaining = this.filteredReports.filter((_, idx) => !assigned.has(idx)); if (remaining.length > 0 && otherRule) { - this.subCategories.push({ name: otherRule.name, count: remaining.length, icon: otherRule.icon, color: otherRule.color, reports: remaining }); + this.subCategories.push({ + name: otherRule.name, + count: remaining.length, + icon: otherRule.icon, + color: otherRule.color, + reports: remaining + }); } } @@ -219,7 +266,7 @@ export class ReportsComponent implements OnInit { this.isSearching = true; this.showSubCategories = false; if (this.selectedSubCategory) { - const subCat = this.subCategories.find(sc => sc.name === this.selectedSubCategory); + const subCat = this.subCategories.find((sc) => sc.name === this.selectedSubCategory); this.setTableData(subCat ? subCat.reports : this.filteredReports); } else if (this.selectedCategory) { this.setTableData(this.filteredReports); @@ -230,7 +277,11 @@ export class ReportsComponent implements OnInit { } else { if (this.selectedSubCategory) { // keep showing sub-cat table - } else if (this.selectedCategory && SUB_CATEGORY_RULES[this.selectedCategory] && this.filteredReports.length > 15) { + } else if ( + this.selectedCategory && + SUB_CATEGORY_RULES[this.selectedCategory] && + this.filteredReports.length > 15 + ) { this.isSearching = false; this.showSubCategories = true; } else { diff --git a/src/app/reports/sanctions-logs/sanctions-logs.component.html b/src/app/reports/sanctions-logs/sanctions-logs.component.html index 8581ef8e0..6d90a0da8 100644 --- a/src/app/reports/sanctions-logs/sanctions-logs.component.html +++ b/src/app/reports/sanctions-logs/sanctions-logs.component.html @@ -1,3 +1,11 @@ + +
@@ -55,7 +58,6 @@
-
Regulatory & Legal
@@ -166,9 +168,13 @@ Sitemap
- App v{{ versions.mifos }} + App v{{ versions.mifos }} · - Build {{ buildDate }} + Build {{ buildDate }}
Powered by LincAI diff --git a/src/app/shared/footer/footer.component.scss b/src/app/shared/footer/footer.component.scss index 8fe159925..fb4fc8a96 100644 --- a/src/app/shared/footer/footer.component.scss +++ b/src/app/shared/footer/footer.component.scss @@ -62,8 +62,8 @@ $accent-2: #10b981; flex-direction: column; padding: 3px 40px; background: #f8f9fa; - border-top: 1px solid rgba(0, 0, 0, 0.06); - border-bottom: 1px solid rgba(0, 0, 0, 0.06); + border-top: 1px solid rgb(0 0 0 / 6%); + border-bottom: 1px solid rgb(0 0 0 / 6%); margin-top: 0; } @@ -91,7 +91,7 @@ $accent-2: #10b981; align-items: center; gap: 10px; padding-right: 20px; - border-right: 1px solid rgba(0, 0, 0, 0.1); + border-right: 1px solid rgb(0 0 0 / 10%); margin-right: 0; } @@ -116,7 +116,7 @@ $accent-2: #10b981; .sec-brand-delimiter { font-size: 12px; - color: rgba(0, 0, 0, 0.2); + color: rgb(0 0 0 / 20%); margin: 0 6px; font-weight: 300; } @@ -200,7 +200,7 @@ $accent-2: #10b981; .ls-label { font-size: 11px; font-weight: 700; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); text-transform: uppercase; letter-spacing: 0.8px; white-space: nowrap; @@ -222,34 +222,34 @@ $accent-2: #10b981; white-space: nowrap; &.green { - background: rgba(16, 185, 129, 0.15); + background: rgb(16 185 129 / 15%); color: #6ee7b7; - border: 1px solid rgba(16, 185, 129, 0.3); + border: 1px solid rgb(16 185 129 / 30%); } &.blue { - background: rgba(14, 165, 233, 0.15); + background: rgb(14 165 233 / 15%); color: #7dd3fc; - border: 1px solid rgba(14, 165, 233, 0.3); + border: 1px solid rgb(14 165 233 / 30%); } &.gold { - background: rgba(245, 158, 11, 0.15); + background: rgb(245 158 11 / 15%); color: #fcd34d; - border: 1px solid rgba(245, 158, 11, 0.3); + border: 1px solid rgb(245 158 11 / 30%); } &.white { - background: rgba(255, 255, 255, 0.08); - color: rgba(255, 255, 255, 0.75); - border: 1px solid rgba(255, 255, 255, 0.15); + background: rgb(255 255 255 / 8%); + color: rgb(255 255 255 / 75%); + border: 1px solid rgb(255 255 255 / 15%); } } // ===== Default Mode (Shell Footer) ===== .app-footer { background: $brand-dark; - color: rgba(255, 255, 255, 0.85); + color: rgb(255 255 255 / 85%); padding: 0; margin-top: 0; position: relative; @@ -258,13 +258,13 @@ $accent-2: #10b981; &::after { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; + inset: 0; background: repeating-linear-gradient( - 108deg, transparent 0, transparent 80px, - rgb(255 255 255 / 2.2%) 80px, rgb(255 255 255 / 2.2%) 160px + 108deg, + transparent 0, + transparent 80px, + rgb(255 255 255 / 2.2%) 80px, + rgb(255 255 255 / 2.2%) 160px ); pointer-events: none; } @@ -302,10 +302,21 @@ $accent-2: #10b981; span { border-radius: 1.5px; - &:nth-child(1) { background: #fff; } - &:nth-child(2) { background: $accent; } - &:nth-child(3) { background: $accent-2; } - &:nth-child(4) { background: rgb(255 255 255 / 30%); } + &:nth-child(1) { + background: #fff; + } + + &:nth-child(2) { + background: $accent; + } + + &:nth-child(3) { + background: $accent-2; + } + + &:nth-child(4) { + background: rgb(255 255 255 / 30%); + } } } @@ -323,7 +334,7 @@ $accent-2: #10b981; .footer-tagline { font-size: 12px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); line-height: 1.5; margin: 0; } @@ -355,13 +366,13 @@ $accent-2: #10b981; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; - color: rgba(255, 255, 255, 0.45); + color: rgb(255 255 255 / 45%); margin: 0 0 2px; } .footer-link { font-size: 12px; - color: rgba(255, 255, 255, 0.75); + color: rgb(255 255 255 / 75%); text-decoration: none; transition: color 0.15s; line-height: 1.5; @@ -383,7 +394,7 @@ $accent-2: #10b981; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; - color: rgba(255, 255, 255, 0.45); + color: rgb(255 255 255 / 45%); margin: 0 0 2px; } @@ -398,13 +409,13 @@ $accent-2: #10b981; align-items: center; gap: 6px; padding: 4px 10px; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); + background: rgb(255 255 255 / 4%); + border: 1px solid rgb(255 255 255 / 8%); border-radius: 5px; transition: background 0.15s; &:hover { - background: rgba(255, 255, 255, 0.08); + background: rgb(255 255 255 / 8%); } } @@ -421,13 +432,13 @@ $accent-2: #10b981; .reg-label { font-size: 11px; - color: rgba(255, 255, 255, 0.75); + color: rgb(255 255 255 / 75%); font-weight: 500; } .reg-link { font-size: 11px; - color: rgba(255, 255, 255, 0.75); + color: rgb(255 255 255 / 75%); text-decoration: none; font-weight: 500; transition: color 0.15s; @@ -447,21 +458,21 @@ $accent-2: #10b981; flex-shrink: 0; &.rbi { - background: rgba(14, 165, 233, 0.2); + background: rgb(14 165 233 / 20%); color: #7dd3fc; - border: 1px solid rgba(14, 165, 233, 0.3); + border: 1px solid rgb(14 165 233 / 30%); } &.circular { - background: rgba(245, 158, 11, 0.15); + background: rgb(245 158 11 / 15%); color: #fcd34d; - border: 1px solid rgba(245, 158, 11, 0.3); + border: 1px solid rgb(245 158 11 / 30%); } &.mandatory { - background: rgba(239, 68, 68, 0.15); + background: rgb(239 68 68 / 15%); color: #fca5a5; - border: 1px solid rgba(239, 68, 68, 0.3); + border: 1px solid rgb(239 68 68 / 30%); } } @@ -477,7 +488,7 @@ $accent-2: #10b981; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; - color: rgba(255, 255, 255, 0.45); + color: rgb(255 255 255 / 45%); margin: 0 0 4px; } @@ -492,14 +503,14 @@ $accent-2: #10b981; align-items: center; gap: 6px; padding: 4px 12px; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); + background: rgb(255 255 255 / 4%); + border: 1px solid rgb(255 255 255 / 8%); border-radius: 6px; text-decoration: none; transition: background 0.15s; &:hover { - background: rgba(255, 255, 255, 0.1); + background: rgb(255 255 255 / 10%); .qn-label { color: #5ec9f8; @@ -515,7 +526,7 @@ $accent-2: #10b981; .qn-label { font-size: 12px; - color: rgba(255, 255, 255, 0.8); + color: rgb(255 255 255 / 80%); font-weight: 500; transition: color 0.15s; } @@ -527,9 +538,9 @@ $accent-2: #10b981; border-radius: 3px; letter-spacing: 0.5px; text-transform: uppercase; - background: rgba(14, 165, 233, 0.2); + background: rgb(14 165 233 / 20%); color: #7dd3fc; - border: 1px solid rgba(14, 165, 233, 0.3); + border: 1px solid rgb(14 165 233 / 30%); flex-shrink: 0; } @@ -545,7 +556,7 @@ $accent-2: #10b981; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; - color: rgba(255, 255, 255, 0.45); + color: rgb(255 255 255 / 45%); margin: 0 0 4px; } @@ -561,14 +572,14 @@ $accent-2: #10b981; align-items: center; gap: 6px; padding: 5px 12px; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); + background: rgb(255 255 255 / 4%); + border: 1px solid rgb(255 255 255 / 8%); border-radius: 5px; transition: background 0.15s; white-space: nowrap; &:hover { - background: rgba(255, 255, 255, 0.08); + background: rgb(255 255 255 / 8%); } &.sc-chat { @@ -594,13 +605,13 @@ $accent-2: #10b981; .sc-label { font-size: 10px; - color: rgba(255, 255, 255, 0.45); + color: rgb(255 255 255 / 45%); font-weight: 500; } .sc-val { font-size: 11px; - color: rgba(255, 255, 255, 0.85); + color: rgb(255 255 255 / 85%); font-weight: 600; &.highlight { @@ -611,7 +622,7 @@ $accent-2: #10b981; // ── Divider ── .footer-divider { height: 1px; - background: rgba(255, 255, 255, 0.1); + background: rgb(255 255 255 / 10%); margin: 0 40px; position: relative; z-index: 1; @@ -630,7 +641,7 @@ $accent-2: #10b981; .fb-copy { font-size: 11px; - color: rgba(255, 255, 255, 0.4); + color: rgb(255 255 255 / 40%); white-space: nowrap; } @@ -641,15 +652,17 @@ $accent-2: #10b981; a { font-size: 11px; - color: rgba(255, 255, 255, 0.5); + color: rgb(255 255 255 / 50%); text-decoration: none; padding: 2px 6px; border-radius: 3px; - transition: color 0.15s, background 0.15s; + transition: + color 0.15s, + background 0.15s; &:hover { color: #5ec9f8; - background: rgba(255, 255, 255, 0.06); + background: rgb(255 255 255 / 6%); } } } @@ -659,18 +672,18 @@ $accent-2: #10b981; align-items: center; gap: 4px; font-size: 10px; - color: rgba(255, 255, 255, 0.35); + color: rgb(255 255 255 / 35%); margin-left: auto; strong { - color: rgba(255, 255, 255, 0.55); + color: rgb(255 255 255 / 55%); font-weight: 600; } } .fb-powered { font-size: 10px; - color: rgba(255, 255, 255, 0.35); + color: rgb(255 255 255 / 35%); strong { color: #5ec9f8; @@ -679,7 +692,7 @@ $accent-2: #10b981; } // ===== Responsive ===== -@media (max-width: 768px) { +@media (width <= 768px) { .security-bar { flex-direction: column; gap: 8px; @@ -730,7 +743,6 @@ $accent-2: #10b981; padding: 12px 20px; } - .footer-divider { margin: 0 20px; } @@ -747,7 +759,7 @@ $accent-2: #10b981; } } -@media (max-width: 480px) { +@media (width <= 480px) { .footer-compact { font-size: 0.7rem; @@ -761,7 +773,6 @@ $accent-2: #10b981; gap: 16px; } - .sec-item { font-size: 10px; } diff --git a/src/app/shared/form-dialog/form-dialog.component.scss b/src/app/shared/form-dialog/form-dialog.component.scss index 5fa85ce2b..31ecb3638 100644 --- a/src/app/shared/form-dialog/form-dialog.component.scss +++ b/src/app/shared/form-dialog/form-dialog.component.scss @@ -175,7 +175,7 @@ $white: #fff; } // Responsive -@media (max-width: 600px) { +@media (width <= 600px) { .fd-grid--cols-2, .fd-grid--cols-3 { grid-template-columns: 1fr; diff --git a/src/app/shared/icons.module.ts b/src/app/shared/icons.module.ts index 1e9cd3f9c..32acaeb75 100644 --- a/src/app/shared/icons.module.ts +++ b/src/app/shared/icons.module.ts @@ -173,7 +173,22 @@ import { faCalendarTimes, faStar, faCrop, - faStamp + faStamp, + faSpinner, + faPhone, + faHashtag, + faMapMarkerAlt, + faHourglassHalf, + faCheckSquare, + faCheckDouble, + faClipboardCheck, + faFileArchive, + faSyncAlt, + faIdCardAlt, + faCube, + faLongArrowAltRight, + faStream, + faThLarge } from '@fortawesome/free-solid-svg-icons'; /** @@ -353,7 +368,22 @@ export class IconsModule { faCalendarTimes, faStar, faCrop, - faStamp + faStamp, + faSpinner, + faPhone, + faHashtag, + faMapMarkerAlt, + faHourglassHalf, + faCheckSquare, + faCheckDouble, + faClipboardCheck, + faFileArchive, + faSyncAlt, + faIdCardAlt, + faCube, + faLongArrowAltRight, + faStream, + faThLarge ); } } diff --git a/src/app/shared/styles/list-layout.scss b/src/app/shared/styles/list-layout.scss index 8af67d841..b16a422cf 100644 --- a/src/app/shared/styles/list-layout.scss +++ b/src/app/shared/styles/list-layout.scss @@ -229,6 +229,60 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; flex-wrap: wrap; } +.table-header-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.table-filters { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + padding: 10px 12px; + background: #f8fafc; + border: 1px solid $border; + border-radius: 10px; + + .table-search { + margin: 0; + } +} + +.table-filter { + min-width: 150px; + max-width: 180px; + margin: 0; + + ::ng-deep { + .mat-mdc-form-field-flex { + height: 38px; + background: #fff; + } + + .mat-mdc-form-field-infix { + padding: 6px 0; + min-height: 38px; + } + + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } + + .mat-mdc-select-value, + .mat-mdc-select-placeholder { + font-size: 13px; + } + } +} + .table-search { flex: 1; min-width: 200px; @@ -268,6 +322,8 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; .table-checkbox { flex-shrink: 0; font-size: 13px; + margin-left: auto; + white-space: nowrap; } .table-buttons { @@ -484,6 +540,15 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; } } +.row-ckyc-icon { + font-size: 16px; + width: 16px; + height: 16px; + vertical-align: middle; + margin-left: 6px; + color: #16a34a; +} + .legal-form-badge { display: inline-flex; align-items: center; diff --git a/src/app/system/audit-trails/audit-trails.component.html b/src/app/system/audit-trails/audit-trails.component.html index f3da5e48b..9d7b0696c 100644 --- a/src/app/system/audit-trails/audit-trails.component.html +++ b/src/app/system/audit-trails/audit-trails.component.html @@ -45,7 +45,10 @@ Status - + @for (processingResult of auditTrailSearchTemplateData.processingResults; track processingResult) { {{ processingResult.processingResult }} } @@ -54,7 +57,12 @@ User - + @@ -63,17 +71,32 @@
Action - + Entity - + Checker - +
@@ -84,7 +107,13 @@
From Date - + @@ -115,7 +144,13 @@
From Date - + @@ -128,7 +163,13 @@
To Date - + @@ -186,7 +227,12 @@
{{ 'labels.inputs.Status' | translate }} - + {{ auditTrail.processingResult }} {{ 'labels.inputs.Entity' | translate }}{{ auditTrail.entityName | translateKey: 'auditTrail.entities' }} + {{ auditTrail.entityName | translateKey: 'auditTrail.entities' }} + {{ 'labels.inputs.Office' | translate }} {{ 'labels.inputs.Checked Date' | translate }}{{ auditTrail.checkedOnDate | datetimeFormat }} + {{ auditTrail.checkedOnDate | datetimeFormat }} + {{ 'labels.inputs.Client Ip' | translate }} {{ 'labels.inputs.Account Number' | translate }} + {{ element.accountNo }} {{ 'labels.inputs.Staff' | translate }} + {{ element.staffName }}
} diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.scss index 73ba0c9bb..80e3ad1f3 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/client-approval/client-approval.component.scss @@ -31,7 +31,9 @@ $text-muted: #64748b; max-width: 320px; ::ng-deep { - .mat-mdc-form-field-flex { height: 32px; } + .mat-mdc-form-field-flex { + height: 32px; + } .mat-mdc-form-field-infix { padding: 4px 0; @@ -44,8 +46,13 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } .mat-mdc-form-field-icon-prefix { @@ -53,7 +60,9 @@ $text-muted: #64748b; color: $text-muted; } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -70,7 +79,7 @@ $text-muted: #64748b; border-radius: 8px; height: 32px; padding: 0 14px; - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; fa-icon { margin-right: 4px; @@ -100,7 +109,7 @@ $text-muted: #64748b; margin-bottom: 12px; } -@media (max-width: 768px) { +@media (width <= 768px) { .cv-action-bar { flex-direction: column; align-items: stretch; diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.html b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.html index 2aa3ade2e..414ab0f40 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.html +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.html @@ -84,7 +84,12 @@
{{ loan.loanPurposeName }}
} diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.scss index 73ba0c9bb..80e3ad1f3 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-approval/loan-approval.component.scss @@ -31,7 +31,9 @@ $text-muted: #64748b; max-width: 320px; ::ng-deep { - .mat-mdc-form-field-flex { height: 32px; } + .mat-mdc-form-field-flex { + height: 32px; + } .mat-mdc-form-field-infix { padding: 4px 0; @@ -44,8 +46,13 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } .mat-mdc-form-field-icon-prefix { @@ -53,7 +60,9 @@ $text-muted: #64748b; color: $text-muted; } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -70,7 +79,7 @@ $text-muted: #64748b; border-radius: 8px; height: 32px; padding: 0 14px; - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; fa-icon { margin-right: 4px; @@ -100,7 +109,7 @@ $text-muted: #64748b; margin-bottom: 12px; } -@media (max-width: 768px) { +@media (width <= 768px) { .cv-action-bar { flex-direction: column; align-items: stretch; diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.html b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.html index 6462e9623..f115e81b6 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.html +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.html @@ -68,7 +68,7 @@ mat-cell *matCellDef="let loan" class="cell-mono" - style="cursor: pointer;" + style="cursor: pointer" [routerLink]="['../../clients', loan.clientId, 'loans-accounts', loan.id, 'general']" > {{ loan.accountNo }} @@ -80,7 +80,7 @@ mat-cell *matCellDef="let loan" [routerLink]="['../../clients', loan.clientId, 'loans-accounts', loan.id, 'general']" - style="cursor: pointer;" + style="cursor: pointer" > {{ loan.loanProductName }}
{{ loan.principal | formatNumber }}
diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.scss index 31c372da8..0b9f62ce1 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component.scss @@ -31,7 +31,9 @@ $text-muted: #64748b; max-width: 320px; ::ng-deep { - .mat-mdc-form-field-flex { height: 32px; } + .mat-mdc-form-field-flex { + height: 32px; + } .mat-mdc-form-field-infix { padding: 4px 0; @@ -44,8 +46,13 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } .mat-mdc-form-field-icon-prefix { @@ -53,7 +60,9 @@ $text-muted: #64748b; color: $text-muted; } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -70,7 +79,7 @@ $text-muted: #64748b; border-radius: 8px; height: 32px; padding: 0 14px; - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; fa-icon { margin-right: 4px; @@ -87,7 +96,7 @@ $text-muted: #64748b; } } -@media (max-width: 768px) { +@media (width <= 768px) { .cv-action-bar { flex-direction: column; align-items: stretch; diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.html b/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.html index 2bb7fd20c..62ad3f06c 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.html +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.html @@ -80,7 +80,7 @@ mat-cell *matCellDef="let loan" class="cell-mono" - style="cursor: pointer;" + style="cursor: pointer" [routerLink]="['../../clients', loan.clientId, 'loans', loan.loanId, 'general']" > {{ loan.loanAccountNumber }} @@ -95,7 +95,12 @@
{{ loan.rescheduleReasonCodeValue.name }}
diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.scss index 35ab4abac..7f73f40b8 100644 --- a/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component.scss @@ -31,7 +31,9 @@ $text-muted: #64748b; max-width: 320px; ::ng-deep { - .mat-mdc-form-field-flex { height: 32px; } + .mat-mdc-form-field-flex { + height: 32px; + } .mat-mdc-form-field-infix { padding: 4px 0; @@ -44,8 +46,13 @@ $text-muted: #64748b; } .mdc-notched-outline { - .mdc-notched-outline__leading { border-radius: 8px 0 0 8px; } - .mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; } + .mdc-notched-outline__leading { + border-radius: 8px 0 0 8px; + } + + .mdc-notched-outline__trailing { + border-radius: 0 8px 8px 0; + } } .mat-mdc-form-field-icon-prefix { @@ -53,7 +60,9 @@ $text-muted: #64748b; color: $text-muted; } - input { font-size: 13px; } + input { + font-size: 13px; + } } } @@ -70,7 +79,7 @@ $text-muted: #64748b; border-radius: 8px; height: 32px; padding: 0 14px; - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; fa-icon { margin-right: 4px; @@ -96,7 +105,7 @@ $text-muted: #64748b; } } -@media (max-width: 768px) { +@media (width <= 768px) { .cv-action-bar { flex-direction: column; align-items: stretch; diff --git a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html index 71f132790..b13591041 100644 --- a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html +++ b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html @@ -17,19 +17,54 @@ diff --git a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.scss b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.scss index e6c6ebca8..14afdfa0e 100644 --- a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.scss +++ b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.scss @@ -19,7 +19,7 @@ ::ng-deep { .mat-mdc-tab-link { - font-family: 'DM Sans', 'Roboto', sans-serif; + font-family: 'DM Sans', Roboto, sans-serif; font-size: 13px; font-weight: 500; letter-spacing: 0.02em; diff --git a/src/app/users/create-user/create-user.component.html b/src/app/users/create-user/create-user.component.html index 463f3a38e..bfa0a7d55 100644 --- a/src/app/users/create-user/create-user.component.html +++ b/src/app/users/create-user/create-user.component.html @@ -29,7 +29,10 @@ {{ 'labels.inputs.Username' | translate }} @if (userForm.controls.username.hasError('required')) { - {{ 'labels.inputs.Username' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Username' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -37,10 +40,16 @@ {{ 'labels.inputs.Email' | translate }} @if (userForm.controls.email.hasError('email')) { - {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.invalid' | translate }} + {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.invalid' | translate }} } @if (userForm.controls.email.hasError('required')) { - {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -48,10 +57,18 @@ {{ 'labels.inputs.First Name' | translate }} @if (userForm.controls.firstname.hasError('pattern')) { - {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.cannot begin with a special character or number' | translate }} + {{ 'labels.inputs.First Name' | translate }} + {{ + 'labels.commons.cannot begin with a special character or number' | translate + }} } @if (userForm.controls.firstname.hasError('required')) { - {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -59,10 +76,18 @@ {{ 'labels.inputs.Last Name' | translate }} @if (userForm.controls.lastname.hasError('pattern')) { - {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.cannot begin with a special character or number' | translate }} + {{ 'labels.inputs.Last Name' | translate }} + {{ + 'labels.commons.cannot begin with a special character or number' | translate + }} } @if (userForm.controls.lastname.hasError('required')) { - {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -90,16 +115,33 @@ {{ 'labels.inputs.Password' | translate }} @if (userForm.controls.password.hasError('required')) { - {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @if (userForm.controls.password.hasError('minlength')) { - {{ 'labels.commons.Password should be at least' | translate }} {{ 'labels.commons.12 characters long' | translate }} + {{ 'labels.commons.Password should be at least' | translate }} + {{ 'labels.commons.12 characters long' | translate }} } @if (userForm.controls.password.hasError('maxlength')) { - {{ 'labels.commons.Password should not be more than' | translate }} {{ 'labels.commons.50 characters long' | translate }} + {{ 'labels.commons.Password should not be more than' | translate }} + {{ 'labels.commons.50 characters long' | translate }} } - @if (userForm.controls.password.hasError('pattern') && !(userForm.controls.password.hasError('minlength') || userForm.controls.password.hasError('maxlength'))) { - {{ 'labels.commons.Password should include a' | translate }} {{ 'labels.commons.numeral' | translate }} {{ 'labels.commons.and at' | translate }} {{ 'labels.commons.least one uppercase' | translate }}, {{ 'labels.commons.lowercase and special character' | translate }} + @if ( + userForm.controls.password.hasError('pattern') && + !(userForm.controls.password.hasError('minlength') || userForm.controls.password.hasError('maxlength')) + ) { + {{ 'labels.commons.Password should include a' | translate }} + {{ 'labels.commons.numeral' | translate }} {{ 'labels.commons.and at' | translate }} + {{ 'labels.commons.least one uppercase' | translate }}, {{ 'labels.commons.lowercase and special character' | translate }} } @@ -107,13 +149,23 @@ {{ 'labels.inputs.Repeat Password' | translate }} @if (userForm.controls.repeatPassword.hasError('required')) { - {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } } - @if (userForm.errors?.passwordsDoNotMatch && !userForm.controls.password.hasError('required') && !userForm.controls.repeatPassword.hasError('required')) { -

{{ 'labels.inputs.Passwords' | translate }} {{ 'labels.commons.do not match' | translate }}

+ @if ( + userForm.errors?.passwordsDoNotMatch && + !userForm.controls.password.hasError('required') && + !userForm.controls.repeatPassword.hasError('required') + ) { +

+ {{ 'labels.inputs.Passwords' | translate }} + {{ 'labels.commons.do not match' | translate }} +

} @@ -133,7 +185,10 @@ } @if (userForm.controls.officeId.hasError('required')) { - {{ 'labels.inputs.Office' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Office' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -154,7 +209,10 @@ } @if (userForm.controls.roles.hasError('required')) { - {{ 'labels.commons.At least one role' | translate }} {{ 'labels.commons.must be selected' | translate }} + {{ 'labels.commons.At least one role' | translate }} + {{ 'labels.commons.must be selected' | translate }} } @@ -176,11 +234,24 @@

{{ 'labels.heading.Create User' | translate }}

-

{{ 'labels.text.Filling Details' | translate }}: {{ 'labels.inputs.Users' | translate }}

+

+ {{ 'labels.text.Filling Details' | translate }}: + {{ + 'labels.inputs.Users' | translate + }} +

- - - - + + + +
diff --git a/src/app/users/create-user/create-user.component.scss b/src/app/users/create-user/create-user.component.scss index f67b187c8..f1efbba06 100644 --- a/src/app/users/create-user/create-user.component.scss +++ b/src/app/users/create-user/create-user.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .cu-field { @@ -148,6 +155,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/users/edit-user/edit-user.component.html b/src/app/users/edit-user/edit-user.component.html index 5320a7681..a5db4c43c 100644 --- a/src/app/users/edit-user/edit-user.component.html +++ b/src/app/users/edit-user/edit-user.component.html @@ -29,7 +29,10 @@ {{ 'labels.inputs.Username' | translate }} @if (editUserForm.controls.username.hasError('required')) { - {{ 'labels.inputs.Username' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Username' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -37,10 +40,16 @@ {{ 'labels.inputs.Email' | translate }} @if (editUserForm.controls.email.hasError('email')) { - {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.invalid' | translate }} + {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.invalid' | translate }} } @if (editUserForm.controls.email.hasError('required')) { - {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -48,10 +57,18 @@ {{ 'labels.inputs.First Name' | translate }} @if (editUserForm.controls.firstname.hasError('pattern')) { - {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.cannot begin with a special character or number' | translate }} + {{ 'labels.inputs.First Name' | translate }} + {{ + 'labels.commons.cannot begin with a special character or number' | translate + }} } @if (editUserForm.controls.firstname.hasError('required')) { - {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -59,10 +76,18 @@ {{ 'labels.inputs.Last Name' | translate }} @if (editUserForm.controls.lastname.hasError('pattern')) { - {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.cannot begin with a special character or number' | translate }} + {{ 'labels.inputs.Last Name' | translate }} + {{ + 'labels.commons.cannot begin with a special character or number' | translate + }} } @if (editUserForm.controls.lastname.hasError('required')) { - {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -98,7 +123,10 @@ } @if (editUserForm.controls.officeId.hasError('required')) { - {{ 'labels.inputs.Office' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} + {{ 'labels.inputs.Office' | translate }} {{ 'labels.commons.is' | translate }} + {{ 'labels.commons.required' | translate }} } @@ -119,7 +147,10 @@ } @if (editUserForm.controls.roles.hasError('required')) { - {{ 'labels.commons.At least one role' | translate }} {{ 'labels.commons.must be selected' | translate }} + {{ 'labels.commons.At least one role' | translate }} + {{ 'labels.commons.must be selected' | translate }} } @@ -130,7 +161,13 @@ - diff --git a/src/app/users/edit-user/edit-user.component.scss b/src/app/users/edit-user/edit-user.component.scss index 7ad59a8a7..c19dcd5c4 100644 --- a/src/app/users/edit-user/edit-user.component.scss +++ b/src/app/users/edit-user/edit-user.component.scss @@ -44,9 +44,14 @@ $gray-900: #202124; border-color: $border !important; color: $gray-500 !important; - i { margin-right: 6px; } + i { + margin-right: 6px; + } - &:hover { color: $primary !important; border-color: $primary !important; } + &:hover { + color: $primary !important; + border-color: $primary !important; + } } // Card @@ -92,7 +97,9 @@ $gray-900: #202124; gap: 4px 16px; padding: 16px 24px; - @media (max-width: 600px) { grid-template-columns: 1fr; } + @media (width <= 600px) { + grid-template-columns: 1fr; + } } .eu-field { @@ -140,6 +147,9 @@ $gray-900: #202124; height: 36px !important; box-shadow: 0 1px 3px rgba($primary, 0.3) !important; - i { margin-right: 6px; font-size: 12px; } + i { + margin-right: 6px; + font-size: 12px; + } } } diff --git a/src/app/users/users.component.html b/src/app/users/users.component.html index 3a235a289..443599f73 100644 --- a/src/app/users/users.component.html +++ b/src/app/users/users.component.html @@ -20,7 +20,12 @@
- +
@@ -63,7 +68,11 @@
{{ 'labels.buttons.Is Self Service' | translate }} - + {{ user.isSelfServiceUser ? ('labels.buttons.Yes' | translate) : ('labels.buttons.No' | translate) }}