diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 703120349..a02f5f9a1 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -66,7 +66,14 @@ "Bash(node node_modules/prettier/bin/prettier.cjs --write \"src/**/*.{ts,html,scss}\")", "Bash(node node_modules/stylelint/bin/stylelint.mjs --fix \"src/**/*.scss\")", "Bash(node node_modules/stylelint/bin/stylelint.mjs \"src/**/*.scss\")", - "Bash(node node_modules/eslint/bin/eslint.js --cache \"src/app/users/users.component.ts\")" + "Bash(node node_modules/eslint/bin/eslint.js --cache \"src/app/users/users.component.ts\")", + "Bash(git stash *)", + "Bash(npx sass *)", + "Bash(awk '/library\\\\.addIcons/{p=1} p' src/app/shared/icons.module.ts)", + "Bash(echo \"EXIT=$?\")", + "Bash(npx lint-staged *)", + "Bash(npx --no-install tsc --noEmit -p tsconfig.json)", + "Bash(npx --no-install prettier --write src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html src/app/clients/edit-client/edit-client.component.html src/app/clients/utils/family-nominee.guard.ts)" ] } } diff --git a/src/app/cfcfrms/_cfc-form.scss b/src/app/cfcfrms/_cfc-form.scss new file mode 100644 index 000000000..da9b266ea --- /dev/null +++ b/src/app/cfcfrms/_cfc-form.scss @@ -0,0 +1,305 @@ +/** + * 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/. + */ + +/* Shared Nexora skin for CFCFRMS form pages (escalate, report-suspect, + report-mule, report-fraud-txn, fmr-submission). Each component @use's + this partial and @include's `cfc-form-skin($accent)`. + + $accent: 'blue' | 'red' — controls the title-bar gradient + submit + button colour. */ + +@mixin cfc-form-skin($accent: 'blue') { + $accent-from: #3b82f6; + $accent-to: #1e40af; + $accent-shadow: rgb(30 64 175 / 30%); + $submit-from: #1e88e5; + $submit-to: #1e40af; + + @if $accent == 'red' { + $accent-from: #ef4444; + $accent-to: #991b1b; + $accent-shadow: rgb(153 27 27 / 35%); + $submit-from: #dc2626; + $submit-to: #991b1b; + } + + /* ─── Card chrome (overrides Material's mat-card) ─────────────────── */ + .report-card, + .escalate-card, + .instructions-card { + border-radius: 14px !important; + border: 1px solid #e2e8f0 !important; + box-shadow: 0 1px 3px rgb(15 23 42 / 4%) !important; + overflow: hidden; + background: #fff; + + ::ng-deep .mat-mdc-card-header { + padding: 0 !important; + margin: 0 !important; + display: block; + background: #f8fafc; + border-bottom: 1px solid #e2e8f0; + } + + ::ng-deep .mat-mdc-card-title { + display: flex !important; + align-items: center; + gap: 10px; + margin: 0 !important; + padding: 14px 18px; + font-size: 14.5px !important; + font-weight: 700 !important; + color: #0d1b2a; + letter-spacing: 0.01em; + line-height: 1; + + /* Promote the FA icon into a small gradient tile. */ + .fa-icon, + fa-icon { + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 8px; + background: linear-gradient(135deg, $accent-from 0%, $accent-to 100%); + color: #fff; + font-size: 13px; + box-shadow: 0 2px 6px -2px $accent-shadow; + margin-right: 4px; + } + } + + ::ng-deep .mat-mdc-card-content { + padding: 22px !important; + background: #fff; + } + } + + /* ─── Form fields (consistent compact density) ────────────────────── */ + .full-width, + .half-width { + width: 100%; + } + + /* Tighten the default Material outlined field — the dashboard hero + + pill tabs are compact, so the form fields should match. */ + ::ng-deep .mat-mdc-form-field { + margin-bottom: 2px; + + .mat-mdc-text-field-wrapper { + background: #fff; + border-radius: 10px; + } + + .mdc-notched-outline__leading, + .mdc-notched-outline__notch, + .mdc-notched-outline__trailing { + border-color: #cbd5e1 !important; + } + + &.mat-focused { + .mdc-notched-outline__leading, + .mdc-notched-outline__notch, + .mdc-notched-outline__trailing { + border-color: $accent-to !important; + border-width: 2px !important; + } + } + + .mat-mdc-form-field-infix { + min-height: 48px; + padding-top: 12px !important; + padding-bottom: 12px !important; + } + + .mat-mdc-form-field-icon-suffix { + padding: 0 10px; + } + } + + /* Datepicker icon size + tone */ + ::ng-deep .mat-datepicker-toggle .mat-mdc-icon-button { + color: #64748b; + } + + /* ─── Form layout helpers ────────────────────────────────────────── */ + .form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + + @media (width <= 720px) { + grid-template-columns: 1fr; + } + } + + .ticket-lookup-row { + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; + align-items: start; + + .ticket-field { + width: 100%; + } + + .lookup-btn { + height: 48px !important; + margin-top: 0; + border-radius: 10px !important; + font-weight: 600 !important; + } + } + + /* ─── Submit button ───────────────────────────────────────────────── */ + .submit-btn { + margin-top: 10px; + height: 46px !important; + border-radius: 12px !important; + font-size: 14px !important; + font-weight: 700 !important; + letter-spacing: 0.02em !important; + background: linear-gradient(135deg, $submit-from 0%, $submit-to 100%) !important; + color: #fff !important; + box-shadow: 0 4px 12px -2px $accent-shadow !important; + transition: + filter 0.15s ease, + transform 0.1s ease; + + &:hover:not(:disabled) { + filter: brightness(1.05); + } + + &:active:not(:disabled) { + transform: translateY(1px); + } + + &:disabled { + opacity: 0.55; + filter: grayscale(0.3); + } + } + + .inline-spinner { + display: inline-block; + margin-right: 8px; + } + + /* ─── Success result panel ───────────────────────────────────────── */ + .success-result { + text-align: center; + padding: 14px 0 6px; + + .success-icon { + width: 64px; + height: 64px; + margin: 0 auto 14px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: linear-gradient(135deg, #34d399 0%, #047857 100%); + color: #fff; + box-shadow: 0 8px 22px -8px rgb(4 120 87 / 50%); + + fa-icon { + font-size: 28px; + } + } + + h3 { + margin: 0 0 18px; + color: #065f46; + font-size: 18px; + font-weight: 800; + letter-spacing: 0.01em; + } + } + + .result-details { + text-align: left; + max-width: 520px; + margin: 0 auto; + background: #f0fdf4; + border: 1px solid #bbf7d0; + border-radius: 12px; + padding: 14px 16px; + } + + .result-row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 8px 0; + border-bottom: 1px dashed #bbf7d0; + font-size: 13px; + + &:last-child { + border-bottom: none; + } + + .result-label { + color: #475569; + font-weight: 500; + } + + .result-value { + font-weight: 700; + color: #0d1b2a; + text-align: right; + + &.mono { + font-family: 'JetBrains Mono', 'Courier New', monospace; + font-size: 12.5px; + color: #1e40af; + } + } + } + + .status-flagged { + color: #b91c1c !important; + font-weight: 800 !important; + text-transform: uppercase; + letter-spacing: 0.04em; + font-size: 12px; + } + + .mr-05 { + margin-right: 6px; + } + + .mt-16 { + margin-top: 18px; + } + + .mono { + font-family: 'JetBrains Mono', 'Courier New', monospace; + font-size: 12.5px; + } +} + +/* ─── Two-column "form + side panel" layout ─────────────────────────── */ +@mixin cfc-side-layout { + .report-layout, + .escalate-layout { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 16px; + + /* Promote to two-column once the right-hand panel exists (escalate). */ + &:has(.instructions-card) { + grid-template-columns: minmax(0, 1fr) 360px; + + @media (width <= 1100px) { + grid-template-columns: minmax(0, 1fr); + } + } + } +} diff --git a/src/app/cfcfrms/cfcfrms-routing.module.ts b/src/app/cfcfrms/cfcfrms-routing.module.ts index 08fe11479..f9cb5d33b 100644 --- a/src/app/cfcfrms/cfcfrms-routing.module.ts +++ b/src/app/cfcfrms/cfcfrms-routing.module.ts @@ -9,6 +9,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { Route } from '../core/route/route.service'; +import { CfcfrmsShellComponent } from './cfcfrms-shell/cfcfrms-shell.component'; import { CfcfrmsDashboardComponent } from './dashboard/cfcfrms-dashboard.component'; import { ComplaintListComponent } from './complaint-list/complaint-list.component'; import { ComplaintDetailComponent } from './complaint-detail/complaint-detail.component'; @@ -23,6 +24,7 @@ const routes: Routes = [ Route.withShell([ { path: 'cfcfrms', + component: CfcfrmsShellComponent, data: { title: 'CFCFRMS', breadcrumb: 'CFCFRMS' }, children: [ { diff --git a/src/app/cfcfrms/cfcfrms-shell/cfcfrms-shell.component.html b/src/app/cfcfrms/cfcfrms-shell/cfcfrms-shell.component.html new file mode 100644 index 000000000..d9fce160f --- /dev/null +++ b/src/app/cfcfrms/cfcfrms-shell/cfcfrms-shell.component.html @@ -0,0 +1,99 @@ + + +
{{ 'labels.text.Query RBI Central Fraud Registry before customer onboarding' | translate }}
-