diff --git a/angular.json b/angular.json index 341741725..9f7f267f0 100644 --- a/angular.json +++ b/angular.json @@ -31,6 +31,8 @@ "@angular/localize/init" ], "assets": [ + "src/favicon.ico", + "src/apple-touch-icon.png", "src/assets/images/cbsnexora-favicon.svg", "src/robots.txt", "src/manifest.json", @@ -39,6 +41,11 @@ "glob": "**/*", "input": "node_modules/tinymce", "output": "assets/tinymce" + }, + { + "glob": "lottie.min.js", + "input": "node_modules/lottie-web/build/player", + "output": "assets/js" } ], "styles": [ diff --git a/package-lock.json b/package-lock.json index 592a9c0cf..066a39243 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "jspdf-autotable": "^5.0.2", "lightgallery": "^2.9.0", "lodash": "4.17.23", + "lottie-web": "^5.13.0", "moment": "^2.29.4", "ngx-image-cropper": "^9.1.6", "ngx-mat-select-search": "^8.0.2", @@ -18380,6 +18381,12 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/lottie-web": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz", + "integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", diff --git a/package.json b/package.json index aad5a8105..ae0ef07c2 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "jspdf-autotable": "^5.0.2", "lightgallery": "^2.9.0", "lodash": "4.17.23", + "lottie-web": "^5.13.0", "moment": "^2.29.4", "ngx-image-cropper": "^9.1.6", "ngx-mat-select-search": "^8.0.2", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2368bb912..eba0b2e26 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -97,6 +97,7 @@ import { TokenInterceptor as ZitadelTokenInterceptor } from './zitadel/token.int import { AuthService } from './zitadel/auth.service'; import { environment } from '../environments/environment'; import { CallbackComponent } from './zitadel/callback/callback.component'; +import { LoadingScreenComponent } from './shared/loading-screen/loading-screen.component'; import { OAuthModule } from 'angular-oauth2-oidc'; export class CustomMissingTranslationHandler implements MissingTranslationHandler { @@ -197,6 +198,7 @@ export function HttpLoaderFactory(http: HttpClient) { AppRoutingModule, NotFoundComponent, CallbackComponent, + LoadingScreenComponent, OAuthModule.forRoot() ], providers: [ diff --git a/src/app/cashier/branch-day/branch-day.component.html b/src/app/cashier/branch-day/branch-day.component.html index 57d49000e..9c13b940d 100644 --- a/src/app/cashier/branch-day/branch-day.component.html +++ b/src/app/cashier/branch-day/branch-day.component.html @@ -352,6 +352,51 @@ } + + @if (preview?.fdInterestDue?.length) { +
+
+ savings + Fixed deposit interest due today +
+ @for (d of preview.fdInterestDue; track d.productId) { +
+ + {{ d.productName }} · {{ d.fdType }} · {{ d.accountCount }} deposit(s) + @if (d.reportStatus === 'APPROVED' || d.reportStatus === 'POSTED') { + {{ d.reportStatus }} + } @else { + {{ d.reportStatus === 'NONE' ? 'NO REPORT' : d.reportStatus }} + } + + ₹{{ d.totalInterest | number: '1.2-2' }} +
+ } +
+ } + @if (preview?.fdInterestPending?.length) { +
+ warning +
+ The day cannot be closed yet. Fixed deposit interest is not approved for: + {{ preview.fdInterestPending.join(', ') }}. Approve it under Reports → FD Interest Posting Reports, then + close the day. The user who generated a report cannot approve it. +
+
+ } + @if (preview?.savingsInterestPending?.length) { +
+ warning +
+ The day cannot be closed yet. Savings interest is not approved for: + {{ preview.savingsInterestPending.join(', ') }}. Approve it under Reports → Interest Posting Reports, then + close the day. +
+
+ } + diff --git a/src/app/cashier/branch-day/branch-day.component.scss b/src/app/cashier/branch-day/branch-day.component.scss index 449dfced1..e8d2b209a 100644 --- a/src/app/cashier/branch-day/branch-day.component.scss +++ b/src/app/cashier/branch-day/branch-day.component.scss @@ -195,6 +195,75 @@ font-size: 11px; color: #94a3b8; } + + /* Names the block, for the interest panels that sit beside the cash arithmetic. */ + &__head { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; + padding-bottom: 6px; + border-bottom: 1px solid #e2e8f0; + font-size: 13px; + color: #0d1b2a; + + mat-icon { + font-size: 18px; + width: 18px; + height: 18px; + color: #1d4ed8; + } + } +} + +/* Interest is money leaving as the day closes, not cash counted in the drawer. */ +.bd-calc--fd { + border-left: 3px solid #1d4ed8; +} + +.bd-fd-ok, +.bd-fd-pending { + display: inline-block; + margin-left: 6px; + padding: 1px 7px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.02em; +} + +.bd-fd-ok { + background: #dcfce7; + color: #15803d; +} + +.bd-fd-pending { + background: #fef3c7; + color: #b45309; +} + +/* Why the close will be refused, said before the count is filled in rather than after. */ +.bd-warn { + display: flex; + align-items: flex-start; + gap: 8px; + max-width: 640px; + margin-bottom: 14px; + padding: 10px 14px; + background: #fffbeb; + border: 1px solid #fcd34d; + border-radius: 8px; + font-size: 12px; + line-height: 1.5; + color: #78350f; + + mat-icon { + flex-shrink: 0; + font-size: 20px; + width: 20px; + height: 20px; + color: #b45309; + } } .bd-hint { diff --git a/src/app/core/shell/shell.component.html b/src/app/core/shell/shell.component.html index cca11e73a..16e462fd8 100644 --- a/src/app/core/shell/shell.component.html +++ b/src/app/core/shell/shell.component.html @@ -503,7 +503,8 @@ [matMenuTriggerFor]="loanOriginationMenu" *mifosxHasPermission="'MENU_LOAN_ORIGINATION'" > - Loan Origination + Loan OriginationLoans - General Ledger + General LedgerGL - Sub Ledger + Sub LedgerSub GL @@ -583,14 +586,16 @@ [matMenuTriggerFor]="reconciliationMenu" *mifosxHasPermission="'MENU_CONFIG_WIZARD'" > - Reconciliation + ReconciliationRecon Cheque Book - Documents Hub + Documents HubDocs Hub @@ -617,77 +622,89 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + +
+ +
+
Core Reports
+ + + + + + + +
+ + +
+
Operations & Interest
+ + + + + + + +
+ + +
+
Compliance & Registers
+ + + + + + + + +
+
- - - - - - - - + +
+
+
Day Operations
+ + + +
+
+
Verification & Reports
+ + +
+
- - - - - - - - - - - - - - - - + + +
+ +
+
Clerk Desk
+ + + + +
+ + +
+
Vouchers
+ + + + +
+ + +
+
Counter & Reports
+ + + +
+
- - - - - - - - - - - - - - + + +
+ +
+
Cash & Counter
+ + + + +
+ + +
+
Transfers & Settlement
+ + + + + +
+ + +
+
Reports & Audit
+ + + + +
+
-
+
Accounts
- - - - - - - - - - + +
+ +
+
CTS Clearing
+ + + +
+ + +
+
Settlement & Recon
+ + + +
+ + +
+
NACH & Direct
+ + + +
+
-
+
Payroll
@@ -1573,23 +1635,33 @@
- - - - - - - + +
+ +
+
KYC Operations
+ + +
+ + +
+
Reports & Screening
+ + + +
+
+
+
Schemes & Social
+ + + + +
+ + +
+
Institutional & Loans
+ + + + +
+
- - - - - - - + +
+ +
+
Payments
+ + + +
+ + +
+
Cards & Mandates
+ + + +
+
diff --git a/src/app/core/shell/shell.component.scss b/src/app/core/shell/shell.component.scss index 1dc00244e..c752a9d22 100644 --- a/src/app/core/shell/shell.component.scss +++ b/src/app/core/shell/shell.component.scss @@ -266,7 +266,7 @@ $color: #e2e4ec; max-width: 100%; overflow-x: hidden; box-sizing: border-box; - padding: 16px 28px; + padding: 10px 18px; } } @@ -282,21 +282,15 @@ $gray-700: #334155; /* ============================================================ Main header (top 2-row navigation) -------------------------------------------------------------- - Redesigned 2026 — clean, modern enterprise SaaS feel: - • Both rows live on the same white surface separated only by a - hairline; r2 has a barely-there blue-50 wash so frequent vs - secondary menus read at a glance without dark/light contrast. - • Items are now equal-width "tabs" with NO vertical dividers - between them (the old cell borders made it look spreadsheet-y). - • Hover / active = a 3 px brand-coloured TOP bar + brand text + - coloured icon. Reads instantly without colour-flooding the row. - • Icons larger (12.5 px) and full-opacity always — easier to scan - during demos. Caret is subtle and rotates on open. + Compact, modern enterprise SaaS navigation: + • Height reduced to 32px per row (64px total) to save screen space. + • Smooth horizontal scrolling prevents menu items from getting cut off. + • Compact typography, icons and paddings fit all banking modules cleanly. ============================================================ */ .main-header { background: #fff; - border-bottom: 2.5px solid $brand; - box-shadow: 0 1px 4px rgb(0 0 0 / 7%); + border-bottom: 2px solid $brand; + box-shadow: 0 1px 3px rgb(0 0 0 / 6%); position: sticky; /* Sit directly under the sticky toolbar (32px tall) so both stay visible. */ @@ -306,19 +300,19 @@ $gray-700: #334155; } .nav-row { - height: 44px; + height: 32px; display: flex; align-items: stretch; - overflow: visible; + overflow: auto visible; position: relative; - - /* Outer frame; vertical dividers between cells are drawn as `border-right` - on each `.ni` below. Switched away from the equal-column grid so each - cell can size to its own label — long names (General Ledger, Documents - Hub) no longer overflow and short ones (Approvals, HRM) no longer waste - space. */ border: 1px solid $gray-200; background: #fff; + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + } } .r2.nav-row { @@ -334,25 +328,17 @@ $gray-700: #334155; background: #f8fafc; } -// ===== Nav Item — modern tab style ===== -// Typography spec — same family / size / weight / color for BOTH rows. +// ===== Nav Item — compact modern tab style ===== .ni { position: relative; display: inline-flex; align-items: center; justify-content: center; - gap: 7px; + gap: 2.5px; height: 100%; - - /* Each cell sizes to its own label (min-width: max-content) and grows - to share any leftover row width. Short labels stay compact, long ones - get the room they need — no overlap, no wasted gap. */ flex: 1 0 auto; - min-width: max-content; - padding: 0 14px; - - /* Vertical divider between adjacent cells; outer frame is the row's - own border, and the last cell drops its right border below. */ + min-width: 0; + padding: 0 4px; border-right: 1px solid $gray-200; cursor: pointer; white-space: nowrap; @@ -360,11 +346,11 @@ $gray-700: #334155; 'IBM Plex Sans', -apple-system, sans-serif; - font-size: 12px; + font-size: 10px; font-weight: 500; color: #0a1a2e; - line-height: 1.3; - letter-spacing: 0.01em; + line-height: 1.2; + letter-spacing: -0.015em; text-decoration: none; user-select: none; transition: @@ -373,17 +359,15 @@ $gray-700: #334155; box-shadow 0.15s ease; border-radius: 0; - /* 3px accent strip that fills on hover / active (uses ::before so we - don't reflow the row when toggling). */ &::before { content: ''; position: absolute; - left: 4px; - right: 4px; + left: 2px; + right: 2px; top: 0; - height: 3px; + height: 2px; background: transparent; - border-radius: 0 0 3px 3px; + border-radius: 0 0 2px 2px; transition: background 0.15s ease; } @@ -410,17 +394,19 @@ $gray-700: #334155; outline-offset: -2px; } - /* Last cell in a row sits flush against the row's own right edge, so - drop its divider to avoid a double 2 px line. */ &:last-child { border-right: none; } + + .ni-short { + display: none; + } } .ni-ico { - width: 12px; - height: 12px; - font-size: 12px; + width: 9.5px; + height: 9.5px; + font-size: 9.5px; color: $gray-500; flex-shrink: 0; transition: color 0.15s ease; @@ -432,11 +418,12 @@ $gray-700: #334155; } .ni-caret { - width: 7px; - height: 7px; - font-size: 7px; + width: 5px; + height: 5px; + font-size: 5px; color: $gray-400; flex-shrink: 0; + margin-left: 0.5px; transition: transform 0.18s ease, color 0.18s ease; @@ -447,17 +434,89 @@ $gray-700: #334155; } } -@media (width <= 1200px) { +@media (width >= 1400px) { .ni { - padding: 0 8px; - gap: 5px; + padding: 0 6px; + gap: 3.5px; + font-size: 10.8px; + letter-spacing: 0; + } + + .ni-ico { + width: 10px; + height: 10px; + font-size: 10px; + } + + .ni-caret { + width: 5.5px; + height: 5.5px; + font-size: 5.5px; + margin-left: 1px; } } -@media (width <= 980px) { +@media (width <= 1200px) { .ni { + padding: 0 3px; + gap: 2px; + font-size: 9.8px; + letter-spacing: -0.02em; + } + + .ni-ico { + width: 9px; + height: 9px; + font-size: 9px; + } + + .ni-caret { + width: 4.5px; + height: 4.5px; + font-size: 4.5px; + } +} + +@media (width <= 1080px) { + .ni { + padding: 0 2.5px; + gap: 1.5px; + font-size: 9.4px; + letter-spacing: -0.025em; + + .ni-full { + display: none; + } + + .ni-short { + display: inline; + } + } + + .ni-ico { + width: 8.5px; + height: 8.5px; + font-size: 8.5px; + } + + .ni-caret { + width: 4px; + height: 4px; + font-size: 4px; + } +} + +@media (width <= 880px) { + .nav-row { + overflow-x: auto; + scrollbar-width: thin; + } + + .ni { + flex: 0 0 auto; padding: 0 6px; - gap: 4px; + gap: 3px; + font-size: 10px; } } @@ -699,21 +758,29 @@ $gray-700: #334155; // ===== Nav Dropdown Menus ===== ::ng-deep .nav-dropdown { - border-radius: 8px !important; + border-radius: 6px !important; border: 1px solid $gray-200 !important; - box-shadow: 0 6px 24px rgb(0 0 0 / 10%) !important; - min-width: 180px !important; - padding: 4px 0 !important; - margin-top: 2px !important; + box-shadow: 0 4px 16px rgb(0 0 0 / 12%) !important; + min-width: 170px !important; + padding: 3px 0 !important; + margin-top: 1px !important; .mat-mdc-menu-item { - font-size: 12.5px !important; + font-size: 10.5px !important; font-weight: 500 !important; - height: 34px !important; - min-height: 34px !important; - padding: 0 14px !important; + height: 25px !important; + min-height: 25px !important; + padding: 0 6px !important; + line-height: 25px !important; color: $gray-700 !important; - font-family: 'DM Sans', Roboto, sans-serif !important; + font-family: 'IBM Plex Sans', Roboto, sans-serif !important; + white-space: nowrap !important; + + .menu-item-icon { + font-size: 10.5px !important; + margin-right: 5px !important; + opacity: 0.8; + } &:hover { background: $brand-hover !important; @@ -728,39 +795,44 @@ $gray-700: #334155; } /* Deposits Mega Panel (inside mat-menu overlay) */ -.deposit-menu-grid { +::ng-deep .deposit-menu-grid { display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 8px 24px; - padding: 8px 12px; - min-width: 600px; + gap: 4px 12px; + padding: 6px 10px; + min-width: 0; + width: 100%; + box-sizing: border-box; + overflow-x: hidden; } -.deposit-menu-group { +::ng-deep .deposit-menu-group { .deposit-group-title { font-weight: 700; - font-size: 11px; + font-size: 9.5px; text-transform: uppercase; - letter-spacing: 0.6px; + letter-spacing: 0.5px; color: #1074b9; - padding: 4px 16px 6px; + padding: 2px 6px 3px; margin-bottom: 2px; - border-bottom: 2px solid #1074b9; + border-bottom: 1.5px solid #1074b9; + white-space: nowrap; } } -/* NPA Mega Panel — 4 columns (Reports / Analysis / Classification / Recovery Management) */ -.npa-grid { - grid-template-columns: repeat(4, minmax(200px, 1fr)); - min-width: 880px; - max-width: 96vw; +/* NPA Mega Panel — 3 columns */ +::ng-deep .npa-grid { + grid-template-columns: repeat(3, minmax(210px, 1fr)); + min-width: 0; + max-width: 100%; + overflow-x: hidden; } -/* Reconciliation Mega Panel — 5 columns, teal accent to match the icon palette */ -.reconciliation-grid { - grid-template-columns: repeat(5, minmax(190px, 1fr)); - min-width: 1040px; - max-width: 96vw; +/* Reconciliation Mega Panel — 5 columns */ +::ng-deep .reconciliation-grid { + grid-template-columns: repeat(5, minmax(170px, 1fr)); + min-width: 0; + max-width: 100%; + overflow-x: hidden; .deposit-group-title { color: #0d9488; @@ -780,47 +852,49 @@ $gray-700: #334155; .gl-mega-menu { position: absolute; - top: 100px; + top: 96px; left: 50%; transform: translateX(-50%); background: #fff; - width: 860px; - max-width: 95vw; - padding: 20px 24px; - box-shadow: 0 6px 24px rgb(0 0 0 / 22%); - border-radius: 0 0 10px 10px; + width: 760px; + max-width: 94vw; + max-height: 72vh; + overflow-y: auto; + padding: 12px 16px; + box-shadow: 0 6px 20px rgb(0 0 0 / 18%); + border-radius: 0 0 8px 8px; z-index: 1000; - border-top: 3px solid #0d47a1; + border-top: 2.5px solid #0d47a1; } .gl-mega-menu-header { display: flex; align-items: center; - gap: 10px; - margin-bottom: 14px; - padding-bottom: 12px; + gap: 8px; + margin-bottom: 8px; + padding-bottom: 6px; border-bottom: 1px solid #e5e7eb; span { - font-size: 18px; + font-size: 14px; font-weight: 700; color: #0d47a1; - letter-spacing: 0.02em; + letter-spacing: 0.01em; } } .gl-mega-menu-header-icon { - font-size: 18px; + font-size: 14px; color: #0d47a1; } .gl-search { width: 100%; - padding: 10px 14px; - margin-bottom: 16px; + padding: 6px 10px; + margin-bottom: 10px; border: 1px solid #d0d5dd; - border-radius: 6px; - font-size: 14px; + border-radius: 5px; + font-size: 12px; box-sizing: border-box; background: #f9fafb; @@ -828,36 +902,37 @@ $gray-700: #334155; outline: none; border-color: #0d47a1; background: #fff; - box-shadow: 0 0 0 3px rgb(13 71 161 / 10%); + box-shadow: 0 0 0 2px rgb(13 71 161 / 10%); } } .gl-columns { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 24px; + gap: 10px 16px; } .gl-group-title { font-weight: 700; - font-size: 11px; + font-size: 10px; text-transform: uppercase; - letter-spacing: 0.8px; + letter-spacing: 0.6px; color: #0d47a1; - margin-bottom: 10px; - padding-bottom: 6px; - border-bottom: 2px solid #0d47a1; + margin-bottom: 5px; + padding-bottom: 3px; + border-bottom: 1.5px solid #0d47a1; } .gl-link { display: flex; align-items: center; - padding: 7px 6px; + padding: 3.5px 5px; text-decoration: none; color: #344054; - font-size: 13px; + font-size: 11.5px; + line-height: 1.3; cursor: pointer; - border-radius: 5px; + border-radius: 4px; transition: background 0.15s, color 0.15s; @@ -873,23 +948,21 @@ $gray-700: #334155; } .gl-icon { - width: 18px; - margin-right: 8px; + width: 14px; + margin-right: 5px; color: #667085; - font-size: 13px; + font-size: 11px; + flex-shrink: 0; .gl-link:hover & { color: #0d47a1; } } -// Marks a menu link as actually wired to a working screen/dialog (vs a still-unwired -// placeholder) — a small green dot at the end of the row, so a reviewer can tell at a glance -// which items in a mega-menu are functional without having to click every one of them. .gl-link--live::after { content: ''; - width: 6px; - height: 6px; + width: 5px; + height: 5px; margin-left: auto; flex-shrink: 0; border-radius: 50%; @@ -899,9 +972,9 @@ $gray-700: #334155; /* RBI Mega Menu */ .rbi-mega-menu { - width: 1100px; - max-width: 97vw; - max-height: 80vh; + width: 880px; + max-width: 94vw; + max-height: 72vh; overflow-y: auto; border-top-color: #b71c1c; } @@ -909,7 +982,7 @@ $gray-700: #334155; .rbi-columns { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 20px; + gap: 10px 16px; } @keyframes move { diff --git a/src/app/core/shell/toolbar/toolbar.component.html b/src/app/core/shell/toolbar/toolbar.component.html index 37321f09d..450624b69 100644 --- a/src/app/core/shell/toolbar/toolbar.component.html +++ b/src/app/core/shell/toolbar/toolbar.component.html @@ -20,7 +20,9 @@
 LIVE
-
Branch: {{ officeLabel }}
+
+ Branch: {{ officeLabel }} +
Date: {{ today | date: 'dd MMM yyyy' }}
@@ -522,7 +524,7 @@
- + + + + @if (isHeadOffice) { + + } +
+
+ + @if (!approvalEnabled) { +
+ +
+ Fixed deposit interest is currently posted directly by the nightly job, so there is nothing to approve here. + Turn on fd-interest-posting-approval under System → Configurations to put deposits behind + this report. +
+
+ } + + @if (isHeadOffice) { +
+ + +
+ @if (tab === 'others') { +
+ +
+ Each branch generates and approves its own reports. The head office can act here only for a branch that has + not opened the day, and only once every other open branch has closed. Anything it approves is marked (HO). +
+
+ } + } + + @if (errorMessage) { +
+ +
{{ errorMessage }}
+
+ } + @if (notice) { +
+ +
{{ notice }}
+
+ } + @if (pendingCount > 0) { +
+ +
+ {{ pendingCount }} report(s) waiting for approval. The day cannot be closed until they are + approved, and the user who generated a report cannot be the one to approve it. +
+
+ } + + @if (due.length > 0) { +
+ +
+
Fixed deposit interest is due but no report has been generated yet for:
+ @for (d of due; track d.productId + '-' + d.officeId) { +
+ {{ d.productName }} + {{ fdTypeShort(d.fdType) }} + + · {{ d.officeName }} · {{ d.postingDate }} · {{ d.accountCount }} deposit(s) · + ₹{{ d.totalInterest | number: '1.2-2' }} + + @if (d.canAct) { + + } @else if (d.blockedReason) { + {{ d.blockedReason }} + } +
+ } +
+
+ } + + +
+
+
Interest due, no report
+
₹{{ dueTotal | number: '1.2-2' }}
+
{{ dueAccountTotal }} deposit(s) · {{ due.length }} product(s)
+
+
+
Awaiting approval
+
₹{{ awaitingTotal | number: '1.2-2' }}
+
{{ awaitingReports.length }} report(s) · blocking day close
+
+
+
Approved — posts at close
+
₹{{ approvedTotal | number: '1.2-2' }}
+
{{ approvedReports.length }} report(s)
+
+
+
Posted today
+
₹{{ postedTotal | number: '1.2-2' }}
+
{{ postedReports.length }} report(s)
+
+
+ + @if (showPreview) { +
+
+
+

FD interest preview — earned up to {{ previewAsOf }}

+
+ What each deposit would be paid if interest were posted today. View only — nothing is saved, approved + or posted. +
+
+
+ Total so far + ₹{{ previewTotal | number: '1.2-2' }} + {{ previewRows.length }} deposit(s) +
+
+
+ + + +
+ @if (previewLoading) { +
Working out what each deposit has earned up to today…
+ } @else { +
+ + + + + + + + + + + + @for (g of previewGroups; track g.officeName + g.productName) { + + + + + + + + } @empty { + + + + } + +
BranchProductSchemeDepositsInterest so far
{{ g.officeName }} + {{ g.productName }} + + {{ fdTypeShort(g.fdType) }} + {{ g.accounts }}₹{{ g.interest | number: '1.2-2' }}
No fixed deposit has interest waiting to be posted.
+
+ +
+ + + + + + + + + + + + + + + + @for (r of previewRows; track r.fdAccountId + '-' + r.interestPostingDate) { + + + + + + + + + + + + } + + + + + + + +
Account noCustomerSchemeInterest forDepositRate %MaturityPays intoInterest so far
{{ r.accountNo }}{{ r.clientName }} + {{ fdTypeShort(r.fdType) }} + {{ r.interestPeriodFrom || '—' }} → {{ r.interestPostingDate }}₹{{ r.depositAmount | number: '1.2-2' }}{{ r.interestRate | number: '1.2-2' }}{{ r.maturityDate || '—' }}{{ r.payoutAccountNo || 'Stays in the deposit' }}₹{{ r.interest | number: '1.2-2' }}
Total + ₹{{ previewTotal | number: '1.2-2' }} +
+
+ } +
+ } + + @if (showOutsideApproval) { +
+
+
+

Fixed deposit interest posted outside this report

+
+ A deposit that matured before its own posting cycle completed has its last period settled directly, the + moment it matures — before any report here could be generated or approved for it. Listed here so + nothing paid to a customer is invisible, approved or not. +
+
+
+ Total + ₹{{ outsideApprovalTotal | number: '1.2-2' }} + {{ outsideApprovalRows.length }} posting(s) +
+
+
+ + + + +
+ @if (outsideApprovalLoading) { +
Looking for deposit interest posted outside this report…
+ } @else { +
+ + + + + + + + + + + @for (g of outsideApprovalGroups; track g.productName) { + + + + + + + } @empty { + + + + } + +
ProductSchemeDepositsInterest
+ {{ g.productName }} + + {{ fdTypeShort(g.fdType) }} + {{ g.accounts }}₹{{ g.interest | number: '1.2-2' }}
+ No fixed deposit interest was posted outside this report in this period. +
+
+ + @if (outsideApprovalRows.length > 0) { +
+ + + + + + + + + + + + + + @for (r of outsideApprovalRows; track r.transactionId) { + + + + + + + + + + } + + + + + + + + +
Account noCustomerProductSchemeInterest forInterestPosted at
{{ r.accountNo }}{{ r.clientName }}{{ r.productName }} + {{ fdTypeShort(r.fdType) }} + {{ r.interestFor }}₹{{ r.interestAmount | number: '1.2-2' }}{{ r.postedAt | date: 'dd MMM yyyy, HH:mm' }}
Total + ₹{{ outsideApprovalTotal | number: '1.2-2' }} +
+
+ } + } +
+ } + + @if (showProductSummary) { +
+
+
+

Interest by product

+
+ Posted through this report, still awaiting approval or posting, and posted directly at maturity instead — + added up to what the product has paid out in total over the period. +
+
+
+ Total, all products + ₹{{ productSummaryGrandTotal.total | number: '1.2-2' }} +
+
+
+ + + + +
+ @if (productSummaryLoading) { +
Adding up interest by product…
+ } @else { +
+ + + + + + + + + + + + + + @for (row of productSummaryRows; track row.productId) { + + + + + + + + + + } @empty { + + + + } + + + + + + + + + + +
ProductSchemePosting periodPosted (report)Pending approvalPosted at maturityTotal
+ {{ row.productName }} + + {{ fdTypeShort(row.fdType) }} + {{ row.postingPeriodLabel || '—' }}₹{{ row.posted | number: '1.2-2' }}₹{{ row.pending | number: '1.2-2' }}₹{{ row.maturityPosted | number: '1.2-2' }} + ₹{{ row.total | number: '1.2-2' }} +
No fixed deposit interest in this period.
Total + ₹{{ productSummaryGrandTotal.posted | number: '1.2-2' }} + + ₹{{ productSummaryGrandTotal.pending | number: '1.2-2' }} + + ₹{{ productSummaryGrandTotal.maturityPosted | number: '1.2-2' }} + + ₹{{ productSummaryGrandTotal.total | number: '1.2-2' }} +
+
+ } +
+ } + +
+
+

Reports{{ postingDate ? ' for ' + postingDate : '' }}

+ One report per scheme, per branch +
+
+ + + + + + + + + + + + + + + + + @for (r of reports; track r.id) { + + + + + + + + + + + + + } @empty { + + + + } + +
BranchSchemeTypePosting dateDepositsTotal interestStatusMakerChecker
{{ r.officeName }} + {{ r.productName }} + + {{ fdTypeShort(r.fdType) }} + {{ r.postingDate }}{{ r.accountCount }}₹{{ r.totalInterest | number: '1.2-2' }} + {{ r.status }} + @if (r.failureReason) { +
Posting failed: {{ r.failureReason }}
+ } +
+ {{ r.generatedBy || 'System' }} · {{ r.generationMode }}
{{ + r.generatedAt | date: 'dd MMM, HH:mm' + }} +
+ @if (r.approvedAt) { + {{ r.approvedBy }} + @if (r.approvedOnBehalf) { + (HO) + } +
{{ r.approvedAt | date: 'dd MMM, HH:mm' }} + } @else { + — + } +
+
+ @if (canRegenerate(r)) { + + } + @if (canApprove(r)) { + + } @else if (r.status === 'GENERATED' && r.approveBlockedReason) { + {{ r.approveBlockedReason }} + } +
+
+ @if (loading) { + Loading… + } @else { + No fixed deposit interest posting report for {{ postingDate || 'this date' }}. Reports are generated + at 12:00 noon on a product's posting date. + } +
+
+
+ + @if (selected) { +
+
+
+

+ {{ selected.officeName }} · {{ selected.productName }} — {{ selected.postingDate }} + {{ fdTypeShort(selected.fdType) }} + {{ selected.postingPeriodLabel }} + {{ selected.status }} +

+
{{ fdTypeLabel(selected.fdType) }}
+
+ Dr Interest Expense → Cr {{ selected.payableGlCode }} {{ selected.payableGlName }}; Dr + {{ selected.payableGlCode }} → Cr {{ selected.paidGlCode }} {{ selected.paidGlName }}; then Dr + {{ selected.paidGlCode }} → Cr each deposit below +
+
+
+ Total interest + ₹{{ selected.totalInterest | number: '1.2-2' }} + {{ selected.accountCount }} deposit(s) +
+
+
+ + + + + + + + + + + + + + + + + + @for (l of selected.lines; track l.fdAccountId + '-' + l.interestPostingDate) { + + + + + + + + + + + + + + } + + + + + + + + +
Account noAccount holderInterest forDepositRate %MaturityInterestBalance beforeBalance afterPays intoPosted txn
{{ l.accountNo }}{{ l.clientName }}{{ l.interestPeriodFrom || '—' }} → {{ l.interestPostingDate }}₹{{ l.depositAmount | number: '1.2-2' }}{{ l.interestRate | number: '1.2-2' }}{{ l.maturityDate || '—' }}₹{{ l.interestAmount | number: '1.2-2' }}₹{{ l.balanceBefore | number: '1.2-2' }}₹{{ l.balanceAfter | number: '1.2-2' }}{{ l.payoutAccountNo || 'Stays in the deposit' }}{{ l.fdTransactionId ? '#' + l.fdTransactionId : '—' }}
Total + ₹{{ selected.totalInterest | number: '1.2-2' }} +
+
+
+ } + diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.scss b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.scss new file mode 100644 index 000000000..1900de118 --- /dev/null +++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.scss @@ -0,0 +1,559 @@ +/** + * 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 { + --fd-navy: #101723; + --fd-accent: #1b4d8f; + --fd-accent-dark: #123c70; + --fd-ground: #f7f8fa; + --fd-border: #dfe4eb; + --fd-border-soft: #edf0f4; + --fd-muted: #5b6676; + --fd-text: #3c4757; + --fd-amber: #6b4600; + --fd-amber-dark: #5a3b00; + --fd-amber-tint: #fdf3dc; + --fd-amber-border: #efd9a3; + --fd-amber-wash: #fffcf4; + --fd-green: #185e38; + --fd-green-dark: #1d6f42; + --fd-green-tint: #e2f3e8; + --fd-green-wash: #f3faf5; + --fd-blue-tint: #e6eef8; + --fd-slate-tint: #edf0f4; + + display: block; + font-family: 'IBM Plex Sans', system-ui, sans-serif; + color: var(--fd-navy); +} + +.fd { + max-width: 1320px; + margin: 0 auto; + padding: 20px 4px 56px; +} + +// ── Header ────────────────────────────────────────────────────────────── + +.fd-head { + display: flex; + flex-wrap: wrap; + gap: 24px; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 22px; +} + +.fd-head-t { + margin: 0; + font-family: 'IBM Plex Serif', serif; + font-weight: 600; + font-size: 26px; + letter-spacing: -0.01em; + display: flex; + align-items: center; + gap: 10px; + + fa-icon { + color: var(--fd-accent); + font-size: 20px; + } +} + +.fd-head-s { + max-width: 720px; + margin-top: 8px; + font-size: 13.5px; + line-height: 1.6; + color: var(--fd-muted); +} + +.fd-head-a { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: 10px; +} + +.fd-date { + display: flex; + flex-direction: column; + gap: 5px; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--fd-muted); + + input { + font-family: 'IBM Plex Mono', monospace; + font-size: 13px; + padding: 9px 11px; + min-height: 44px; + border: 1px solid #cdd5df; + border-radius: 7px; + background: #fff; + color: var(--fd-navy); + } +} + +// ── Buttons ───────────────────────────────────────────────────────────── + +.fd-btn { + font-family: inherit; + font-size: 12.5px; + font-weight: 500; + padding: 8px 14px; + min-height: 44px; + border-radius: 7px; + border: 1px solid #cdd5df; + background: #fff; + color: var(--fd-navy); + cursor: pointer; + white-space: nowrap; + + fa-icon { + margin-right: 6px; + } + + &:hover:not([disabled]) { + background: #f2f5f9; + } + + &[disabled] { + opacity: 0.5; + cursor: not-allowed; + } + + &--primary { + background: var(--fd-accent); + border-color: var(--fd-accent); + color: #fff; + + &:hover:not([disabled]) { + background: var(--fd-accent-dark); + } + } + + &--slim { + min-height: 34px; + padding: 6px 11px; + font-size: 12px; + } +} + +// ── Tabs ──────────────────────────────────────────────────────────────── + +.fd-tabs { + display: flex; + gap: 4px; + margin-bottom: 20px; + border-bottom: 1px solid var(--fd-border); +} + +.fd-tab { + font-family: inherit; + font-size: 13px; + font-weight: 500; + padding: 10px 6px; + min-height: 44px; + border: 0; + border-bottom: 2px solid transparent; + background: transparent; + color: var(--fd-muted); + cursor: pointer; + + fa-icon { + margin-right: 7px; + } + + &.is-active { + border-bottom-color: var(--fd-accent); + color: var(--fd-accent); + font-weight: 600; + } + + & + .fd-tab { + margin-left: 18px; + } +} + +// ── Alerts ────────────────────────────────────────────────────────────── + +.fd-alert { + display: flex; + gap: 12px; + align-items: flex-start; + margin-bottom: 18px; + padding: 14px 18px; + border-radius: 10px; + font-size: 13px; + line-height: 1.6; + + fa-icon { + margin-top: 2px; + flex-shrink: 0; + } + + &--info { + background: #eef3fa; + border: 1px solid #c8d8ec; + color: #1f3a5c; + } + + &--warn { + background: var(--fd-amber-tint); + border: 1px solid var(--fd-amber-border); + color: var(--fd-amber-dark); + + strong { + color: var(--fd-amber-dark); + } + } + + &--ok { + background: var(--fd-green-wash); + border: 1px solid #bfe3cc; + color: var(--fd-green); + } + + &--error { + background: #fdecea; + border: 1px solid #f3b8b1; + color: #8a2b22; + } +} + +.fd-due-body { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; +} + +.fd-due-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + padding: 8px 0; + + & + .fd-due-row { + border-top: 1px dashed var(--fd-amber-border); + } +} + +.fd-blocked { + font-size: 11.5px; + color: var(--fd-muted); +} + +// ── Summary strip ─────────────────────────────────────────────────────── + +.fd-summary { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 16px; + margin-bottom: 20px; + + @media (width <= 860px) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +.fd-stat { + background: #fff; + border: 1px solid var(--fd-border); + border-radius: 12px; + padding: 18px 20px; + + &__label { + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--fd-muted); + } + + &__value { + font-family: 'IBM Plex Mono', monospace; + font-variant-numeric: tabular-nums; + font-size: 25px; + font-weight: 500; + letter-spacing: -0.02em; + margin-top: 9px; + } + + &__sub { + font-size: 11.5px; + color: var(--fd-muted); + margin-top: 4px; + } + + &--warn { + border-color: var(--fd-amber-border); + + .fd-stat__label, + .fd-stat__sub { + color: var(--fd-amber); + } + + .fd-stat__value { + color: var(--fd-amber-dark); + } + } + + &--ok { + .fd-stat__label { + color: var(--fd-green); + } + + .fd-stat__value { + color: var(--fd-green); + } + } + + &--posted { + .fd-stat__label { + color: var(--fd-accent); + } + + .fd-stat__value { + color: var(--fd-accent); + } + } +} + +// ── Cards ─────────────────────────────────────────────────────────────── + +.fd-card { + background: #fff; + border: 1px solid var(--fd-border); + border-radius: 12px; + margin-bottom: 20px; + overflow: hidden; +} + +.fd-card-h { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: baseline; + padding: 16px 20px; + border-bottom: 1px solid var(--fd-border); + + h2, + h3 { + margin: 0; + font-size: 15px; + font-weight: 600; + } +} + +.fd-card-sub { + font-size: 12px; + color: var(--fd-muted); +} + +// ── Tables ────────────────────────────────────────────────────────────── + +.fd-table-wrap { + overflow-x: auto; +} + +.fd-table { + border-collapse: collapse; + width: 100%; + + th { + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--fd-muted); + text-align: left; + padding: 10px 14px; + border-bottom: 1px solid var(--fd-border); + white-space: nowrap; + } + + td { + font-size: 13px; + padding: 12px 14px; + border-bottom: 1px solid var(--fd-border-soft); + vertical-align: top; + } + + tbody tr:last-child td { + border-bottom: 0; + } + + tbody tr.is-clickable { + cursor: pointer; + + &:hover td { + background: #fafbfd; + } + } + + tbody tr.is-selected td { + background: var(--fd-blue-tint); + } + + tfoot tr { + background: var(--fd-ground); + } + + .num { + text-align: right; + font-family: 'IBM Plex Mono', monospace; + font-variant-numeric: tabular-nums; + white-space: nowrap; + } +} + +.fd-mono { + font-family: 'IBM Plex Mono', monospace; + font-variant-numeric: tabular-nums; +} + +.fd-sub { + font-size: 11.5px; + color: var(--fd-muted); + margin-top: 3px; +} + +.fd-empty { + text-align: center; + padding: 30px 16px; + color: var(--fd-muted); + font-size: 13px; +} + +// ── Chips & badges ────────────────────────────────────────────────────── + +.fd-chip { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.02em; + white-space: nowrap; + + &--single { + background: var(--fd-slate-tint); + color: var(--fd-text); + } + + &--double { + background: var(--fd-blue-tint); + color: var(--fd-accent); + } + + &--special { + background: var(--fd-amber-tint); + color: var(--fd-amber); + } +} + +.fd-badge { + display: inline-block; + padding: 2px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.02em; + white-space: nowrap; + + &--pending { + background: var(--fd-amber-tint); + color: var(--fd-amber); + } + + &--approved { + background: var(--fd-green-tint); + color: var(--fd-green); + } + + &--posted { + background: var(--fd-blue-tint); + color: var(--fd-accent); + } + + &--muted { + background: var(--fd-slate-tint); + color: var(--fd-muted); + } +} + +.fd-behalf { + font-size: 10.5px; + font-weight: 700; + color: var(--fd-accent); + margin-left: 4px; +} + +.fd-fail { + margin-top: 4px; + font-size: 11px; + color: #a32b22; +} + +// ── Detail panel ──────────────────────────────────────────────────────── + +.fd-detail-head { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: space-between; + align-items: flex-start; + padding: 22px 20px; + border-bottom: 1px solid var(--fd-border-soft); + background: #fbfcfd; + + h3 { + margin: 0; + font-family: 'IBM Plex Serif', serif; + font-weight: 600; + font-size: 19px; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 9px; + } +} + +.fd-total { + text-align: right; + + span:first-child { + display: block; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--fd-muted); + } + + strong { + display: block; + font-family: 'IBM Plex Mono', monospace; + font-variant-numeric: tabular-nums; + font-size: 26px; + font-weight: 500; + margin-top: 6px; + letter-spacing: -0.02em; + } +} + +.muted { + color: var(--fd-muted); + font-size: 12.5px; + margin-top: 4px; +} diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts new file mode 100644 index 000000000..38ff43ae1 --- /dev/null +++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.component.ts @@ -0,0 +1,686 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, OnInit, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { SettingsService } from 'app/settings/settings.service'; +import { exportWorkbook } from '../cashier/cashier-export'; +import { + FdInterestDue, + FdInterestPreviewRow, + FdInterestReport, + FdInterestReportDetail, + FdInterestPostingReportService, + FdInterestSummaryRow, + FdOutsideApprovalRow, + FdReportScope, + FdType +} from './fd-interest-posting-report.service'; + +/** + * Fixed deposit interest posting reports. + * + * On a product's posting date the system generates, at noon, the list of what each deposit will be + * paid. One user of the branch reviews it and a second approves it — the maker cannot be the checker, + * because posting interest moves money to customers and, for a special deposit, straight out to their + * savings account. The approved amounts are posted when the branch closes the day, and until then it + * cannot close. Regenerating replaces the report and needs approving again. + */ +@Component({ + selector: 'mifosx-fd-interest-posting-report', + templateUrl: './fd-interest-posting-report.component.html', + styleUrls: ['./fd-interest-posting-report.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FormsModule, + FaIconComponent + ] +}) +export class FdInterestPostingReportComponent implements OnInit { + private reportService = inject(FdInterestPostingReportService); + private settingsService = inject(SettingsService); + + postingDate = ''; + loading = false; + busy = false; + errorMessage = ''; + notice = ''; + + reports: FdInterestReport[] = []; + selected: FdInterestReportDetail | null = null; + /** Products with interest due and no report yet — the noon job ran before they had anything due, or failed. */ + due: FdInterestDue[] = []; + + /** 'own' — the user's own branch; 'others' — every other branch, a head-office-only tab. */ + tab: FdReportScope = 'own'; + isHeadOffice = false; + myBranch = ''; + /** False where the bank has deposits on the old direct-posting path; every list here is then empty by design. */ + approvalEnabled = true; + + ngOnInit(): void { + this.postingDate = this.businessDateIso(); + this.reportService.context().subscribe({ + next: (ctx) => { + this.isHeadOffice = !!ctx?.headOffice; + this.myBranch = ctx?.officeName ?? ''; + this.approvalEnabled = ctx?.approvalEnabled !== false; + }, + error: () => (this.isHeadOffice = false) + }); + this.load(); + } + + switchTab(tab: FdReportScope): void { + if (this.tab === tab) { + return; + } + this.tab = tab; + this.selected = null; + this.notice = ''; + this.load(); + } + + /** Acting for another branch is the head office's exception, so it is spelt out before it happens. */ + private confirmOnBehalf(officeName: string, action: string): boolean { + if (this.tab !== 'others') { + return true; + } + return confirm( + `${officeName} has not opened today. You are about to ${action} its fixed deposit interest posting report on its ` + + 'behalf, from the head office. Continue?' + ); + } + + load(): void { + this.loading = true; + this.errorMessage = ''; + this.reportService.list(this.postingDate || undefined, undefined, undefined, this.tab).subscribe({ + next: (reports) => { + this.reports = reports ?? []; + this.loading = false; + const stillThere = this.selected && this.reports.find((r) => r.id === this.selected?.id); + if (stillThere) { + this.open(stillThere); + } else { + this.selected = null; + } + }, + error: (err) => { + this.loading = false; + this.errorMessage = this.messageOf(err, 'Could not load the fixed deposit interest posting reports.'); + } + }); + this.reportService.dueWithoutReport(this.postingDate || undefined, this.tab).subscribe({ + next: (due) => (this.due = due ?? []), + error: () => (this.due = []) + }); + } + + generate(item: FdInterestDue): void { + if (!this.confirmOnBehalf(item.officeName, 'generate')) { + return; + } + this.busy = true; + this.notice = ''; + this.errorMessage = ''; + this.reportService.regenerate(item.productId, item.officeId, item.postingDate).subscribe({ + next: (res) => { + this.busy = false; + this.notice = `Report generated for ${item.productName} — ${item.officeName}. Another user of the branch has to approve it.`; + this.load(); + this.reportService.detail(res.reportId).subscribe((detail) => (this.selected = detail)); + }, + error: (err) => { + this.busy = false; + this.errorMessage = this.messageOf(err, 'Could not generate the report.'); + } + }); + } + + open(report: FdInterestReport): void { + this.errorMessage = ''; + this.reportService.detail(report.id).subscribe({ + next: (detail) => (this.selected = detail), + error: (err) => (this.errorMessage = this.messageOf(err, 'Could not load the report.')) + }); + } + + regenerate(report: FdInterestReport): void { + if ( + !confirm( + `Regenerate the fixed deposit interest posting report for ${report.productName} — ${report.officeName}? ` + + 'It will need approving again.' + ) || + !this.confirmOnBehalf(report.officeName, 'regenerate') + ) { + return; + } + this.busy = true; + this.notice = ''; + this.errorMessage = ''; + this.reportService.regenerate(report.productId, report.officeId, report.postingDate).subscribe({ + next: (res) => { + this.busy = false; + this.notice = `Report regenerated for ${report.productName}. Another user of the branch has to approve it.`; + this.selected = null; + this.load(); + this.reportService.detail(res.reportId).subscribe((detail) => (this.selected = detail)); + }, + error: (err) => { + this.busy = false; + this.errorMessage = this.messageOf(err, 'Could not regenerate the report.'); + } + }); + } + + approve(report: FdInterestReport): void { + const payout = + report.fdType === 'SPECIAL' + ? ' This is a monthly-income product, so the interest is paid out to the customers’ linked savings accounts.' + : ''; + if ( + !confirm( + `Approve interest of ₹${report.totalInterest} across ${report.accountCount} deposit(s) for ${report.productName}?` + + `${payout} It will be posted when the branch closes the day.` + ) || + !this.confirmOnBehalf(report.officeName, 'approve') + ) { + return; + } + this.busy = true; + this.notice = ''; + this.errorMessage = ''; + this.reportService.approve(report.id).subscribe({ + next: () => { + this.busy = false; + this.notice = `Approved. Interest for ${report.productName} — ${report.officeName} will be posted when the branch closes the day.`; + this.load(); + }, + error: (err) => { + this.busy = false; + this.errorMessage = this.messageOf(err, 'Could not approve the report.'); + } + }); + } + + canRegenerate(report: FdInterestReport): boolean { + return report.canAct && (report.status === 'GENERATED' || report.status === 'APPROVED'); + } + + /** + * The server decides this, not the screen: it alone knows who generated the report, and the maker + * cannot be the checker. + */ + canApprove(report: FdInterestReport): boolean { + return !!report.canApprove; + } + + statusClass(status: string): string { + return ( + { + GENERATED: 'fd-badge--pending', + APPROVED: 'fd-badge--approved', + POSTED: 'fd-badge--posted', + SUPERSEDED: 'fd-badge--muted' + }[status] ?? 'fd-badge--muted' + ); + } + + /** What the scheme means for the customer, spelt out rather than left as a code. */ + fdTypeLabel(fdType: FdType | string): string { + return ( + { + SINGLE: 'Single — interest paid at maturity', + DOUBLE: 'Double — interest compounded into the deposit', + SPECIAL: 'Special — interest paid monthly to linked savings' + }[fdType] ?? String(fdType ?? '') + ); + } + + fdTypeShort(fdType: FdType | string): string { + return ( + { + SINGLE: 'Single', + DOUBLE: 'Double', + SPECIAL: 'Special' + }[fdType] ?? String(fdType ?? '') + ); + } + + fdTypeClass(fdType: FdType | string): string { + return ( + { + SINGLE: 'fd-chip--single', + DOUBLE: 'fd-chip--double', + SPECIAL: 'fd-chip--special' + }[fdType] ?? 'fd-chip--single' + ); + } + + /** Pending reports that block this user's own day close; another branch's pending reports block only that branch. */ + get pendingCount(): number { + return this.tab === 'own' ? this.reports.filter((r) => r.status === 'GENERATED').length : 0; + } + + // ── Summary strip: the day's reports, at a glance ── + + get dueTotal(): number { + return this.due.reduce((sum, d) => sum + Number(d.totalInterest || 0), 0); + } + + get dueAccountTotal(): number { + return this.due.reduce((sum, d) => sum + Number(d.accountCount || 0), 0); + } + + private reportsWithStatus(status: string): FdInterestReport[] { + return this.reports.filter((r) => r.status === status); + } + + private totalOf(list: FdInterestReport[]): number { + return list.reduce((sum, r) => sum + Number(r.totalInterest || 0), 0); + } + + get awaitingReports(): FdInterestReport[] { + return this.reportsWithStatus('GENERATED'); + } + + get approvedReports(): FdInterestReport[] { + return this.reportsWithStatus('APPROVED'); + } + + get postedReports(): FdInterestReport[] { + return this.reportsWithStatus('POSTED'); + } + + get awaitingTotal(): number { + return this.totalOf(this.awaitingReports); + } + + get approvedTotal(): number { + return this.totalOf(this.approvedReports); + } + + get postedTotal(): number { + return this.totalOf(this.postedReports); + } + + // ── Preview: what each deposit has earned so far, as if posted today. View only. ── + + showPreview = false; + previewLoading = false; + previewRows: FdInterestPreviewRow[] = []; + previewAsOf = ''; + + openPreview(): void { + this.showPreview = true; + this.previewLoading = true; + this.errorMessage = ''; + this.previewAsOf = this.businessDateIso(); + this.reportService.preview().subscribe({ + next: (rows) => { + this.previewRows = rows ?? []; + this.previewLoading = false; + }, + error: (err) => { + this.previewLoading = false; + this.previewRows = []; + this.errorMessage = this.messageOf(err, 'Could not load the fixed deposit interest preview.'); + } + }); + } + + closePreview(): void { + this.showPreview = false; + } + + get previewTotal(): number { + return this.previewRows.reduce((sum, r) => sum + Number(r.interest || 0), 0); + } + + /** Totals by branch, product and scheme, for the summary above the deposit list and the export. */ + get previewGroups(): { + officeName: string; + productName: string; + fdType: string; + accounts: number; + interest: number; + }[] { + const map = new Map< + string, + { officeName: string; productName: string; fdType: string; accounts: number; interest: number } + >(); + this.previewRows.forEach((r) => { + const key = `${r.officeName}|${r.productName}`; + const g = map.get(key) ?? { + officeName: r.officeName, + productName: r.productName, + fdType: r.fdType, + accounts: 0, + interest: 0 + }; + g.accounts += 1; + g.interest += Number(r.interest || 0); + map.set(key, g); + }); + return [...map.values()]; + } + + exportPreview(): void { + const stamp = `Fixed deposit interest earned to ${this.previewAsOf} — preview, not posted`; + exportWorkbook( + [ + { + sheetName: 'By deposit', + title: 'Fixed Deposit Interest Preview', + subtitle: stamp, + headers: [ + 'Branch', + 'Product', + 'Scheme', + 'Account No', + 'Customer', + 'Interest For', + 'Deposit', + 'Rate %', + 'Maturity', + 'Balance', + 'Pays Into', + 'Interest So Far' + ], + widths: [ + 18, + 22, + 10, + 20, + 26, + 12, + 16, + 8, + 12, + 16, + 18, + 16 + ], + rows: [ + ...this.previewRows.map((r) => [ + r.officeName, + r.productName, + this.fdTypeShort(r.fdType), + r.accountNo, + r.clientName, + r.interestPostingDate ?? '', + Number(r.depositAmount || 0), + Number(r.interestRate || 0), + r.maturityDate ?? '', + Number(r.balance || 0), + r.payoutAccountNo ?? '', + Number(r.interest || 0) + ]), + [ + 'TOTAL', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + this.previewTotal + ] + ] + }, + { + sheetName: 'By branch & product', + title: 'Fixed Deposit Interest Preview — Totals', + subtitle: stamp, + headers: [ + 'Branch', + 'Product', + 'Scheme', + 'Deposits', + 'Interest So Far' + ], + widths: [ + 18, + 22, + 10, + 10, + 16 + ], + rows: [ + ...this.previewGroups.map((g) => [ + g.officeName, + g.productName, + this.fdTypeShort(g.fdType), + g.accounts, + g.interest + ]), + [ + 'TOTAL', + '', + '', + this.previewRows.length, + this.previewTotal + ] + ] + } + ], + `fd-interest-preview-${this.previewAsOf}` + ); + } + + // ── Outside approval: deposit interest posted without this report, chiefly at maturity ── + + showOutsideApproval = false; + outsideApprovalLoading = false; + outsideApprovalRows: FdOutsideApprovalRow[] = []; + outsideApprovalFrom = ''; + outsideApprovalTo = ''; + + openOutsideApproval(): void { + this.showOutsideApproval = true; + if (!this.outsideApprovalTo) { + this.outsideApprovalTo = this.businessDateIso(); + this.outsideApprovalFrom = `${this.outsideApprovalTo.slice(0, 7)}-01`; + } + this.loadOutsideApproval(); + } + + loadOutsideApproval(): void { + this.outsideApprovalLoading = true; + this.errorMessage = ''; + this.reportService + .outsideApproval(this.outsideApprovalFrom || undefined, this.outsideApprovalTo || undefined, undefined, this.tab) + .subscribe({ + next: (rows) => { + this.outsideApprovalRows = rows ?? []; + this.outsideApprovalLoading = false; + }, + error: (err) => { + this.outsideApprovalLoading = false; + this.outsideApprovalRows = []; + this.errorMessage = this.messageOf(err, 'Could not load deposit interest posted outside this report.'); + } + }); + } + + closeOutsideApproval(): void { + this.showOutsideApproval = false; + } + + get outsideApprovalTotal(): number { + return this.outsideApprovalRows.reduce((sum, r) => sum + Number(r.interestAmount || 0), 0); + } + + /** Totals by product and scheme, so a branch can see at a glance where its unapproved interest sits. */ + get outsideApprovalGroups(): { productName: string; fdType: string; accounts: number; interest: number }[] { + const map = new Map(); + this.outsideApprovalRows.forEach((r) => { + const g = map.get(r.productName) ?? { productName: r.productName, fdType: r.fdType, accounts: 0, interest: 0 }; + g.accounts += 1; + g.interest += Number(r.interestAmount || 0); + map.set(r.productName, g); + }); + return [...map.values()]; + } + + // ── Product summary: due for the product vs posted through this report vs posted at maturity ── + // Head office only, same as the branch-wise summary it reuses — a branch reads its own due and + // posted totals off the strip and tables above, which already cover it. + + showProductSummary = false; + productSummaryLoading = false; + productSummaryFrom = ''; + productSummaryTo = ''; + private summaryRows: FdInterestSummaryRow[] = []; + private productOutsideRows: FdOutsideApprovalRow[] = []; + + openProductSummary(): void { + this.showProductSummary = true; + if (!this.productSummaryTo) { + this.productSummaryTo = this.businessDateIso(); + this.productSummaryFrom = `${this.productSummaryTo.slice(0, 7)}-01`; + } + this.loadProductSummary(); + } + + loadProductSummary(): void { + this.productSummaryLoading = true; + this.errorMessage = ''; + let pending = 2; + const done = () => { + pending -= 1; + if (pending === 0) this.productSummaryLoading = false; + }; + this.reportService.summary(this.productSummaryFrom || undefined, this.productSummaryTo || undefined).subscribe({ + next: (rows) => { + this.summaryRows = rows ?? []; + done(); + }, + error: (err) => { + this.summaryRows = []; + this.errorMessage = this.messageOf(err, 'Could not load the product summary.'); + done(); + } + }); + this.reportService + .outsideApproval(this.productSummaryFrom || undefined, this.productSummaryTo || undefined) + .subscribe({ + next: (rows) => { + this.productOutsideRows = rows ?? []; + done(); + }, + error: () => { + this.productOutsideRows = []; + done(); + } + }); + } + + closeProductSummary(): void { + this.showProductSummary = false; + } + + /** + * One row per product and scheme: what posted through this report (and what is still pending + * approval or posting), plus what posted outside it — chiefly a deposit's last interest, paid the + * moment it matured rather than through a periodic report. + */ + get productSummaryRows(): { + productId: number; + productName: string; + fdType: string; + postingPeriodLabel: string; + posted: number; + pending: number; + maturityPosted: number; + total: number; + }[] { + const map = new Map< + number, + { + productId: number; + productName: string; + fdType: string; + postingPeriodLabel: string; + posted: number; + pending: number; + maturityPosted: number; + total: number; + } + >(); + const get = (productId: number, productName: string, fdType: string, label: string) => { + const existing = map.get(productId); + if (existing) return existing; + const created = { + productId, + productName, + fdType, + postingPeriodLabel: label, + posted: 0, + pending: 0, + maturityPosted: 0, + total: 0 + }; + map.set(productId, created); + return created; + }; + this.summaryRows.forEach((r) => { + const row = get(r.productId, r.productName, r.fdType, r.postingPeriodLabel); + const amount = Number(r.totalInterest || 0); + if (r.status === 'POSTED') { + row.posted += amount; + } else if (r.status === 'GENERATED' || r.status === 'APPROVED') { + row.pending += amount; + } + }); + this.productOutsideRows.forEach((r) => { + const row = get(r.productId, r.productName, r.fdType, ''); + row.maturityPosted += Number(r.interestAmount || 0); + }); + map.forEach((row) => { + row.total = row.posted + row.pending + row.maturityPosted; + }); + return [...map.values()].sort((a, b) => a.productName.localeCompare(b.productName)); + } + + get productSummaryGrandTotal(): { posted: number; pending: number; maturityPosted: number; total: number } { + return this.productSummaryRows.reduce( + (sum, r) => ({ + posted: sum.posted + r.posted, + pending: sum.pending + r.pending, + maturityPosted: sum.maturityPosted + r.maturityPosted, + total: sum.total + r.total + }), + { posted: 0, pending: 0, maturityPosted: 0, total: 0 } + ); + } + + private businessDateIso(): string { + const businessDate = this.settingsService.businessDate as any; + const parsed = businessDate ? new Date(businessDate) : new Date(); + if (isNaN(parsed.getTime())) { + return new Date().toISOString().slice(0, 10); + } + const month = String(parsed.getMonth() + 1).padStart(2, '0'); + const day = String(parsed.getDate()).padStart(2, '0'); + return `${parsed.getFullYear()}-${month}-${day}`; + } + + private messageOf(err: any, fallback: string): string { + return ( + err?.error?.errors?.[0]?.defaultUserMessage ?? err?.error?.defaultUserMessage ?? err?.error?.message ?? fallback + ); + } +} diff --git a/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts b/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts new file mode 100644 index 000000000..72d12f397 --- /dev/null +++ b/src/app/fd-interest-posting-report/fd-interest-posting-report.service.ts @@ -0,0 +1,280 @@ +/** + * 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 { Injectable, inject } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +export type FdInterestReportStatus = 'GENERATED' | 'APPROVED' | 'POSTED' | 'SUPERSEDED'; + +/** Which branches a list covers: the user's own, or (head office only) every other branch. */ +export type FdReportScope = 'own' | 'others'; + +/** The deposit scheme, which decides where a posting of interest ends up. */ +export type FdType = 'SINGLE' | 'DOUBLE' | 'SPECIAL'; + +export interface FdInterestReport { + id: number; + productId: number; + productName: string; + fdType: FdType; + officeId: number; + officeName: string; + postingDate: string; + status: FdInterestReportStatus; + totalInterest: number; + accountCount: number; + generationMode: 'AUTO' | 'MANUAL'; + generatedAt: string; + generatedBy: string | null; + approvedAt: string | null; + approvedBy: string | null; + approvedOnBehalf: boolean; + postedAt: string | null; + failureReason: string | null; + /** The product's posting cycle in words — Monthly, Quarterly, Half-yearly, Yearly. */ + postingPeriodLabel: string; + /** Whether this user may regenerate it: their own branch, or the head office for a branch that did not open. */ + canAct: boolean; + blockedReason: string | null; + /** Whether this user may approve it — false for the user who generated it, who cannot be its checker. */ + canApprove: boolean; + approveBlockedReason: string | null; +} + +export interface FdInterestReportLine { + fdAccountId: number; + accountNo: string; + clientName: string; + /** + * The day after the deposit's last paid period ended, or its activation date for a first-ever + * posting — null for a line posted before this was tracked. Paired with interestPostingDate, it is + * the span the interest was earned over. + */ + interestPeriodFrom: string | null; + /** + * The period this interest is for, as the interest engine dated it — not the day the branch closes. + * A deposit owed two unpaid periods appears on two lines, one per date. + */ + interestPostingDate: string; + interestAmount: number; + balanceBefore: number; + balanceAfter: number; + depositAmount: number | null; + interestRate: number | null; + maturityDate: string | null; + /** SPECIAL only: the linked savings account this interest is swept to after posting. */ + payoutSavingsAccountId: number | null; + payoutAccountNo: string | null; + payoutTransactionId: number | null; + fdTransactionId: number | null; +} + +export interface FdInterestReportDetail extends FdInterestReport { + payableGlCode: string | null; + payableGlName: string | null; + paidGlCode: string | null; + paidGlName: string | null; + lines: FdInterestReportLine[]; +} + +export interface FdInterestDue { + productId: number; + productName: string; + fdType: FdType; + officeId: number; + officeName: string; + postingDate: string; + accountCount: number; + totalInterest: number; + canAct: boolean; + blockedReason: string | null; +} + +export interface FdInterestSummaryRow { + id: number; + postingDate: string; + officeId: number; + officeName: string; + productId: number; + productName: string; + fdType: FdType; + accountCount: number; + totalInterest: number; + status: FdInterestReportStatus; + approvedBy: string | null; + approvedOnBehalf: boolean; + approvedAt: string | null; + postedAt: string | null; + postingPeriodLabel: string; +} + +/** + * One fixed deposit interest transaction that reached the customer's account without this report's + * approval — chiefly a deposit that matured before its own posting cycle completed, whose last period + * is settled directly by the maturity job. View only, for visibility. + */ +export interface FdOutsideApprovalRow { + transactionId: number; + fdAccountId: number; + accountNo: string; + clientName: string; + officeId: number; + officeName: string; + productId: number; + productName: string; + fdType: FdType; + interestFor: string; + interestAmount: number; + /** When the posting actually reached the account — not the period it is for. */ + postedAt: string; +} + +export interface FdInterestPreviewRow { + fdAccountId: number; + accountNo: string; + clientName: string; + officeId: number; + officeName: string; + productId: number; + productName: string; + fdType: FdType; + /** The day after the deposit's last paid period ended, or its activation date for the first one. */ + interestPeriodFrom: string | null; + /** The period this interest belongs to, as the engine dated it. */ + interestPostingDate: string; + depositAmount: number | null; + interestRate: number | null; + maturityDate: string | null; + balance: number; + interest: number; + payoutAccountNo: string | null; +} + +/** One product's deposits due on the day being closed, as the day-close screen shows them. */ +export interface FdDayCloseDueRow { + productId: number; + productName: string; + fdType: FdType; + accountCount: number; + totalInterest: number; + reportStatus: FdInterestReportStatus | 'NONE'; +} + +export interface FdDayCloseCheck { + businessDate: string; + canClose: boolean; + pendingProducts: string[]; + due: FdDayCloseDueRow[]; +} + +/** + * The fixed deposit interest posting reports, one per branch and product: generated at noon on a + * posting date, approved by a second user of the branch, posted when the branch closes the day. + */ +@Injectable({ providedIn: 'root' }) +export class FdInterestPostingReportService { + private http = inject(HttpClient); + private readonly base = '/fixeddepositinterestpostingreports'; + + /** scope 'own' is the user's own branch; 'others' (head office only) every other branch. */ + list( + postingDate?: string, + status?: string, + officeId?: number, + scope?: FdReportScope + ): Observable { + let params = new HttpParams(); + if (scope) params = params.set('scope', scope); + if (postingDate) params = params.set('postingDate', postingDate); + if (status) params = params.set('status', status); + if (officeId) params = params.set('officeId', String(officeId)); + return this.http.get(this.base, { params }); + } + + /** Branch and product pairs with deposit interest due by the date and no report yet. */ + dueWithoutReport(postingDate?: string, scope?: FdReportScope): Observable { + let params = new HttpParams(); + if (scope) params = params.set('scope', scope); + if (postingDate) params = params.set('postingDate', postingDate); + return this.http.get(`${this.base}/due-without-report`, { params }); + } + + /** + * The signed-in user's branch, whether it is the head office, and whether deposits are on the + * approval path at all — with that switched off these screens are empty by design. + */ + context(): Observable<{ officeId: number; officeName: string; headOffice: boolean; approvalEnabled: boolean }> { + return this.http.get<{ officeId: number; officeName: string; headOffice: boolean; approvalEnabled: boolean }>( + `${this.base}/context` + ); + } + + detail(reportId: number): Observable { + return this.http.get(`${this.base}/${reportId}`); + } + + regenerate( + productId: number, + officeId: number, + postingDate: string + ): Observable<{ reportId: number; status: string }> { + return this.http.post<{ reportId: number; status: string }>( + this.base, + { productId, officeId, postingDate }, + { params: new HttpParams().set('command', 'regenerate') } + ); + } + + approve(reportId: number): Observable<{ reportId: number; status: string }> { + return this.http.post<{ reportId: number; status: string }>( + `${this.base}/${reportId}`, + {}, + { params: new HttpParams().set('command', 'approve') } + ); + } + + /** What each deposit has earned and not been paid, as if posted today. View only. */ + preview(officeId?: number): Observable { + let params = new HttpParams(); + if (officeId) params = params.set('officeId', String(officeId)); + return this.http.get(`${this.base}/preview`, { params }); + } + + /** What stops a branch closing its day, and the deposits behind it. */ + dayCloseCheck(date?: string, officeId?: number): Observable { + let params = new HttpParams(); + if (date) params = params.set('date', date); + if (officeId) params = params.set('officeId', String(officeId)); + return this.http.get(`${this.base}/day-close-check`, { params }); + } + + /** The head office's branch-wise view over a date range. */ + summary(fromDate?: string, toDate?: string, productId?: number): Observable { + let params = new HttpParams(); + if (fromDate) params = params.set('fromDate', fromDate); + if (toDate) params = params.set('toDate', toDate); + if (productId) params = params.set('productId', String(productId)); + return this.http.get(`${this.base}/summary`, { params }); + } + + /** Deposit interest posted without this report's approval — a deposit maturing mid-cycle, chiefly. */ + outsideApproval( + fromDate?: string, + toDate?: string, + officeId?: number, + scope?: FdReportScope + ): Observable { + let params = new HttpParams(); + if (scope) params = params.set('scope', scope); + if (fromDate) params = params.set('fromDate', fromDate); + if (toDate) params = params.set('toDate', toDate); + if (officeId) params = params.set('officeId', String(officeId)); + return this.http.get(`${this.base}/outside-approval`, { params }); + } +} diff --git a/src/app/products/products.component.scss b/src/app/products/products.component.scss index b7997935e..a52eb7707 100644 --- a/src/app/products/products.component.scss +++ b/src/app/products/products.component.scss @@ -15,43 +15,43 @@ $gray-900: #0f172a; // ── Container ── .pd-container { - max-width: 1100px; + max-width: 1280px; margin: 0 auto; - padding: 24px 20px; + padding: 12px 16px; } // ── Page Header ── .pd-header { display: flex; align-items: center; - gap: 16px; - margin-bottom: 28px; + gap: 10px; + margin-bottom: 14px; &__icon { - width: 44px; - height: 44px; - border-radius: 10px; + width: 34px; + height: 34px; + border-radius: 7px; background: linear-gradient(135deg, #0d3d78, #1a5276); display: flex; align-items: center; justify-content: center; color: #fff; - font-size: 18px; + font-size: 14px; flex-shrink: 0; } &__title { - font-size: 20px; + font-size: 16px; font-weight: 700; color: $gray-900; margin: 0; - letter-spacing: -0.3px; + letter-spacing: -0.2px; } &__sub { - font-size: 13px; + font-size: 11.5px; color: $gray-500; - margin: 2px 0 0; + margin: 1px 0 0; } } @@ -59,7 +59,7 @@ $gray-900: #0f172a; .pd-grid { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 12px; + gap: 8px; @media (width <= 768px) { grid-template-columns: 1fr; @@ -70,11 +70,11 @@ $gray-900: #0f172a; .pd-card { display: flex; align-items: center; - gap: 14px; - padding: 14px 16px; + gap: 10px; + padding: 8px 12px; background: #fff; border: 1px solid $border; - border-radius: 10px; + border-radius: 7px; text-decoration: none; cursor: pointer; transition: all 0.15s ease; @@ -82,7 +82,7 @@ $gray-900: #0f172a; &:hover { border-color: #cbd5e1; background: $gray-50; - box-shadow: 0 2px 8px rgb(0 0 0 / 4%); + box-shadow: 0 1px 6px rgb(0 0 0 / 4%); .pd-card__arrow { color: $gray-700; @@ -91,13 +91,13 @@ $gray-900: #0f172a; } &__icon { - width: 40px; - height: 40px; - border-radius: 9px; + width: 30px; + height: 30px; + border-radius: 6px; display: flex; align-items: center; justify-content: center; - font-size: 15px; + font-size: 12.5px; flex-shrink: 0; &--blue { @@ -167,25 +167,25 @@ $gray-900: #0f172a; } &__title { - font-size: 13.5px; + font-size: 12px; font-weight: 600; color: $gray-900; margin: 0; - line-height: 1.3; + line-height: 1.25; } &__desc { - font-size: 11.5px; + font-size: 10.5px; color: $gray-400; - margin: 3px 0 0; - line-height: 1.35; + margin: 1px 0 0; + line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } &__arrow { - font-size: 11px; + font-size: 10px; color: #cbd5e1; flex-shrink: 0; transition: all 0.15s ease; diff --git a/src/app/reports/reports-routing.module.ts b/src/app/reports/reports-routing.module.ts index af43e1032..0fb1f75c1 100644 --- a/src/app/reports/reports-routing.module.ts +++ b/src/app/reports/reports-routing.module.ts @@ -92,6 +92,14 @@ const routes: Routes = [ (m) => m.InterestPostingReportComponent ) }, + { + path: 'fd-interest-posting', + data: { title: 'FD Interest Posting Reports', breadcrumb: 'FD Interest Posting Reports' }, + loadComponent: () => + import('../fd-interest-posting-report/fd-interest-posting-report.component').then( + (m) => m.FdInterestPostingReportComponent + ) + }, { path: 'dormancy', data: { title: 'Inactive, Dormant & DEAF', breadcrumb: 'Inactive, Dormant & DEAF' }, diff --git a/src/app/shared/loading-screen/loading-screen.component.html b/src/app/shared/loading-screen/loading-screen.component.html new file mode 100644 index 000000000..1a53bec62 --- /dev/null +++ b/src/app/shared/loading-screen/loading-screen.component.html @@ -0,0 +1,80 @@ + + +@if (active) { + @if (currentMode === 'overlay') { + +
+ +
+ } + + @if (currentMode === 'fullscreen') { + +
+
+ CBS Nexora + Core Banking System +
+ +
+ @if (!lottieLoaded) { +
+
+
+
+
+ } +
+ +
+
+
+ +

{{ message || 'Connecting to Core Banking Engine...' }}

+
+ } + + @if (currentMode === 'inline') { + +
+
+ @if (!lottieLoaded) { +
+
+
+
+
+ } +
+ @if (message) { +

{{ message }}

+ } +
+ } +} diff --git a/src/app/shared/loading-screen/loading-screen.component.scss b/src/app/shared/loading-screen/loading-screen.component.scss new file mode 100644 index 000000000..345478be2 --- /dev/null +++ b/src/app/shared/loading-screen/loading-screen.component.scss @@ -0,0 +1,349 @@ +/** + * 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 { + display: block; +} + +/* ========================================================================== + Overlay Mode (MifosProgressIndicatorOverlay / MifosLoadingDialog) + ========================================================================== */ +.cbs-loading-backdrop { + position: fixed; + inset: 0; + z-index: 99999; + display: flex; + align-items: center; + justify-content: center; + background: rgb(9 13 22 / 72%); + backdrop-filter: blur(8px); + animation: backdrop-fade-in 0.25s ease-out forwards; +} + +.cbs-loading-dialog { + position: relative; + width: 90%; + max-width: 340px; + padding: 28px 24px; + background: #111827; + color: #f8fafc; + border-radius: 20px; + border: 1px solid rgb(255 255 255 / 12%); + box-shadow: + 0 25px 60px -15px rgb(0 0 0 / 65%), + 0 0 35px rgb(37 99 235 / 18%); + text-align: center; + overflow: hidden; + animation: dialog-scale-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +:host-context([data-theme='light']) .cbs-loading-dialog, +:host-context(.light-theme) .cbs-loading-dialog { + background: #fff; + color: #0f172a; + border-color: rgb(0 0 0 / 8%); + box-shadow: + 0 25px 50px -12px rgb(0 0 0 / 25%), + 0 0 25px rgb(37 99 235 / 12%); +} + +.cbs-loading-dialog__content { + margin-top: 10px; + margin-bottom: 16px; +} + +.cbs-loading-dialog__title { + font-size: 1.05rem; + font-weight: 700; + letter-spacing: -0.01em; + margin: 0 0 6px; +} + +.cbs-loading-dialog__message { + font-size: 0.82rem; + color: #94a3b8; + line-height: 1.45; + margin: 0; +} + +:host-context([data-theme='light']) .cbs-loading-dialog__message, +:host-context(.light-theme) .cbs-loading-dialog__message { + color: #64748b; +} + +/* Pulse bar at dialog bottom */ +.cbs-loading-pulse-bar { + width: 100%; + height: 3px; + background: rgb(255 255 255 / 8%); + border-radius: 99px; + overflow: hidden; + position: relative; + margin-top: 6px; +} + +.cbs-pulse-bar__indicator { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 40%; + background: linear-gradient(90deg, #2563eb, #38bdf8); + border-radius: 99px; + animation: pulse-bar-slide 1.4s infinite ease-in-out; +} + +/* ========================================================================== + Full-Screen Mode (App Boot / Splash) + ========================================================================== */ +.cbs-fullscreen-loader { + position: fixed; + inset: 0; + z-index: 99999; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: radial-gradient(circle at center, #0f172a 0%, #090d16 100%); + color: #f8fafc; + padding: 24px; + text-align: center; +} + +:host-context([data-theme='light']) .cbs-fullscreen-loader, +:host-context(.light-theme) .cbs-fullscreen-loader { + background: radial-gradient(circle at center, #fff 0%, #f1f5f9 100%); + color: #0f172a; +} + +.cbs-fullscreen-loader__brand { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +.cbs-fullscreen-loader__logo-img { + height: 52px; + width: auto; + max-width: 260px; + object-fit: contain; + filter: drop-shadow(0 6px 18px rgb(0 0 0 / 45%)); +} + +.cbs-brand-icon { + width: 64px; + height: 64px; + border-radius: 16px; + background: linear-gradient(135deg, #1e3a8a 0%, #0284c7 100%); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 10px 24px rgb(14 165 233 / 35%); + margin-bottom: 6px; +} + +.cbs-brand-name { + font-size: 1.5rem; + font-weight: 800; + letter-spacing: -0.02em; + margin: 0; +} + +.cbs-brand-sub { + font-size: 0.8rem; + color: #94a3b8; + font-weight: 500; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.cbs-progress-track { + width: 240px; + height: 5px; + background: rgb(255 255 255 / 10%); + border-radius: 99px; + overflow: hidden; + margin: 18px 0 12px; + position: relative; +} + +.cbs-progress-fill { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 45%; + background: linear-gradient(90deg, #2563eb, #38bdf8); + border-radius: 99px; + box-shadow: 0 0 12px rgb(56 189 248 / 50%); + animation: pulse-bar-slide 1.5s infinite ease-in-out; +} + +.cbs-fullscreen-loader__status { + font-size: 0.82rem; + color: #94a3b8; + font-family: inherit; + margin: 0; +} + +/* ========================================================================== + Inline Mode + ========================================================================== */ +.cbs-inline-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 32px 16px; + text-align: center; +} + +.cbs-inline-loader__message { + font-size: 0.82rem; + color: #94a3b8; + margin-top: 10px; +} + +/* ========================================================================== + Lottie & Vector Spinner Dimensions + ========================================================================== */ +.cbs-lottie-container { + width: 110px; + height: 110px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; +} + +.cbs-lottie-container--large { + width: 140px; + height: 140px; +} + +.cbs-lottie-container--compact { + width: 64px; + height: 64px; +} + +/* Vector Spinner Fallback (CSS Multi-Ring Loader) */ +.cbs-vector-spinner { + width: 72px; + height: 72px; + position: relative; +} + +.cbs-vector-spinner--large { + width: 96px; + height: 96px; +} + +.cbs-vector-spinner--compact { + width: 44px; + height: 44px; +} + +.spinner-ring { + position: absolute; + inset: 0; + border-radius: 50%; + border: 3px solid transparent; +} + +.ring-outer { + border-top-color: #2563eb; + border-right-color: #2563eb; + animation: spin-outer 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; +} + +.ring-mid { + inset: 6px; + border-bottom-color: #0ea5e9; + border-left-color: #0ea5e9; + animation: spin-mid 1.6s cubic-bezier(0.5, 0, 0.5, 1) infinite reverse; +} + +.ring-inner { + inset: 13px; + border-top-color: #10b981; + animation: spin-inner 0.9s cubic-bezier(0.5, 0, 0.5, 1) infinite; +} + +/* ========================================================================== + Keyframe Animations + ========================================================================== */ +@keyframes backdrop-fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes dialog-scale-in { + from { + opacity: 0; + transform: scale(0.92); + } + + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes pulse-bar-slide { + 0% { + left: -40%; + width: 30%; + } + + 50% { + left: 35%; + width: 50%; + } + + 100% { + left: 100%; + width: 30%; + } +} + +@keyframes spin-outer { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +@keyframes spin-mid { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +@keyframes spin-inner { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} diff --git a/src/app/shared/loading-screen/loading-screen.component.ts b/src/app/shared/loading-screen/loading-screen.component.ts new file mode 100644 index 000000000..34f0f5b0d --- /dev/null +++ b/src/app/shared/loading-screen/loading-screen.component.ts @@ -0,0 +1,168 @@ +/** + * 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, + Input, + ElementRef, + ViewChild, + AfterViewInit, + inject, + ChangeDetectorRef +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Subscription } from 'rxjs'; +import { LoadingScreenService, LoadingConfig } from './loading-screen.service'; + +declare const lottie: any; + +@Component({ + selector: 'mifosx-loading-screen', + standalone: true, + imports: [CommonModule], + templateUrl: './loading-screen.component.html', + styleUrls: ['./loading-screen.component.scss'] +}) +export class LoadingScreenComponent implements OnInit, AfterViewInit, OnDestroy { + private readonly loadingService = inject(LoadingScreenService); + private readonly cdr = inject(ChangeDetectorRef); + + @Input() mode: 'overlay' | 'inline' | 'fullscreen' = 'overlay'; + @Input() title: string = 'CBS Nexora'; + @Input() message: string = 'Processing transaction...'; + @Input() active: boolean = false; + @Input() isGlobalOverlay: boolean = true; + + @ViewChild('lottieContainer') lottieContainerRef?: ElementRef; + + currentMode: 'overlay' | 'inline' | 'fullscreen' = 'overlay'; + lottieLoaded: boolean = false; + private animInstance: any = null; + private subscription?: Subscription; + + ngOnInit(): void { + this.currentMode = this.mode; + + if (this.isGlobalOverlay) { + this.subscription = new Subscription(); + this.subscription.add( + this.loadingService.isVisible$.subscribe((visible) => { + this.active = visible; + this.cdr.markForCheck(); + if (visible) { + setTimeout(() => this.initLottie(), 50); + } else { + this.destroyLottie(); + } + }) + ); + + this.subscription.add( + this.loadingService.config$.subscribe((cfg: LoadingConfig) => { + if (cfg.title !== undefined) this.title = cfg.title; + if (cfg.message !== undefined) this.message = cfg.message; + if (cfg.mode !== undefined) this.currentMode = cfg.mode; + this.cdr.markForCheck(); + }) + ); + } + } + + ngAfterViewInit(): void { + if (this.active) { + this.initLottie(); + } + } + + ngOnDestroy(): void { + this.subscription?.unsubscribe(); + this.destroyLottie(); + } + + preventDismiss(event: MouseEvent): void { + event.stopPropagation(); + } + + private initLottie(): void { + if (!this.lottieContainerRef?.nativeElement) return; + this.destroyLottie(); + + const container = this.lottieContainerRef.nativeElement; + + // Check if lottie-web global is available + if (typeof lottie !== 'undefined') { + try { + this.animInstance = lottie.loadAnimation({ + container, + renderer: 'svg', + loop: true, + autoplay: true, + path: 'assets/animations/loading_animation.json' + }); + this.lottieLoaded = true; + this.cdr.markForCheck(); + return; + } catch (err) { + console.warn('Could not load Lottie animation, using vector fallback', err); + } + } + + // Try dynamic script load of local asset if not loaded yet + this.loadLottieScript() + .then(() => { + if (typeof lottie !== 'undefined' && this.lottieContainerRef?.nativeElement) { + this.animInstance = lottie.loadAnimation({ + container: this.lottieContainerRef.nativeElement, + renderer: 'svg', + loop: true, + autoplay: true, + path: 'assets/animations/loading_animation.json' + }); + this.lottieLoaded = true; + this.cdr.markForCheck(); + } + }) + .catch(() => { + this.lottieLoaded = false; + this.cdr.markForCheck(); + }); + } + + private loadLottieScript(): Promise { + return new Promise((resolve, reject) => { + if (typeof lottie !== 'undefined') { + resolve(); + return; + } + + const existingScript = document.getElementById('cbs-lottie-script'); + if (existingScript) { + existingScript.addEventListener('load', () => resolve()); + return; + } + + const script = document.createElement('script'); + script.id = 'cbs-lottie-script'; + script.src = 'assets/js/lottie.min.js'; + script.onload = () => resolve(); + script.onerror = () => reject(); + document.head.appendChild(script); + }); + } + + private destroyLottie(): void { + if (this.animInstance) { + try { + this.animInstance.destroy(); + } catch (_) {} + this.animInstance = null; + } + } +} diff --git a/src/app/shared/loading-screen/loading-screen.service.ts b/src/app/shared/loading-screen/loading-screen.service.ts new file mode 100644 index 000000000..7952a3ea6 --- /dev/null +++ b/src/app/shared/loading-screen/loading-screen.service.ts @@ -0,0 +1,61 @@ +/** + * 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 { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; + +export interface LoadingConfig { + title?: string; + message?: string; + mode?: 'overlay' | 'inline' | 'fullscreen'; +} + +@Injectable({ + providedIn: 'root' +}) +export class LoadingScreenService { + private readonly visibleSubject = new BehaviorSubject(false); + private readonly configSubject = new BehaviorSubject({ + title: 'CBS Nexora', + message: 'Processing transaction...', + mode: 'overlay' + }); + + readonly isVisible$: Observable = this.visibleSubject.asObservable(); + readonly config$: Observable = this.configSubject.asObservable(); + + /** + * Show the loading screen/overlay + * @param message Optional message to display (e.g. "Processing Transfer...") + * @param title Optional title to display (defaults to "CBS Nexora") + * @param mode Optional display mode ('overlay' | 'fullscreen') + */ + show(message?: string, title?: string, mode: 'overlay' | 'fullscreen' = 'overlay'): void { + this.configSubject.next({ + title: title || 'CBS Nexora', + message: message || 'Please wait...', + mode + }); + this.visibleSubject.next(true); + } + + /** + * Hide the loading screen/overlay + */ + hide(): void { + this.visibleSubject.next(false); + } + + /** + * Update message while loading screen is active + */ + setMessage(message: string): void { + const current = this.configSubject.getValue(); + this.configSubject.next({ ...current, message }); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 7135ffff2..46dc2d16e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -30,6 +30,7 @@ import { SearchToolComponent } from './search-tool/search-tool.component'; import { KeyboardShortcutsDialogComponent } from './keyboard-shortcuts-dialog/keyboard-shortcuts-dialog.component'; import { ServerSelectorComponent } from './server-selector/server-selector.component'; import { TenantSelectorComponent } from './tenant-selector/tenant-selector.component'; +import { LoadingScreenComponent } from './loading-screen/loading-screen.component'; /** Custom Modules */ import { IconsModule } from './icons.module'; @@ -116,9 +117,11 @@ import { DatetimePickerStubComponent } from './datetime-picker-stub/datetime-pic DropdownComponent, InputAmountComponent, InputPasswordComponent, + LoadingScreenComponent, DatetimePickerStubComponent // temporarily added stub for datetime picker ], exports: [ + LoadingScreenComponent, FileUploadComponent, FooterComponent, LanguageSelectorComponent, diff --git a/src/app/web-app.component.html b/src/app/web-app.component.html index 7b7f1edc1..c9a9e5647 100644 --- a/src/app/web-app.component.html +++ b/src/app/web-app.component.html @@ -7,3 +7,4 @@ --> + diff --git a/src/app/web-app.component.ts b/src/app/web-app.component.ts index 5645ad422..f91f850c1 100644 --- a/src/app/web-app.component.ts +++ b/src/app/web-app.component.ts @@ -319,6 +319,11 @@ export class WebAppComponent implements OnInit, OnDestroy { }); }); } + + // Dismiss initial application boot splash screen smoothly + if (typeof (window as any).hideAppSplash === 'function') { + (window as any).hideAppSplash(); + } } /** Stores the server's "today" where every date default reads it (see core/utils/business-today). */ diff --git a/src/assets/animations/error_animation.json b/src/assets/animations/error_animation.json new file mode 100644 index 000000000..25ba45887 --- /dev/null +++ b/src/assets/animations/error_animation.json @@ -0,0 +1,2755 @@ +{ + "v": "4.6.3", + "fr": 24, + "ip": 0, + "op": 21, + "w": 320, + "h": 320, + "nm": "checklist", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "Shape Layer 13", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 300 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "Shape Layer 12", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 250 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "Shape Layer 11", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 200 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 5, + "ty": 4, + "nm": "Shape Layer 10", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 150 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 6, + "ty": 4, + "nm": "Shape Layer 9", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 100 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 7, + "ty": 4, + "nm": "Shape Layer 8", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 50 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 8, + "ty": 4, + "nm": "Shape Layer 7", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 160, + 159.5, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + -34, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667 + ], "y": [ + 1, + 1 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p667_1_0p167_0p167", + "0p667_1_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 17 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.667, "y": 1 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p667_1_0p167_0p167", + "t": 6, + "s": [ + -8.142, + -92.147 + ], + "e": [ + -7.675, + -162.544 + ], + "to": [ + 0.07779947668314, + -11.7327470779419 + ], + "ti": [ + -0.07779947668314, + 11.7327470779419 + ] + }, + { "t": 17 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 83.981, + 100 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p667_1_0p167_0p167"], + "t": 6, + "s": [20.367], + "e": [6.367] + }, + { + "t": 17 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.167, + 0.167 + ], "y": [ + 0.167, + 0.167 + ] }, "n": [ + "0p833_0p833_0p167_0p167", + "0p833_0p833_0p167_0p167" + ], "t": 6, "s": [ + 15.021, + 15.021 + ], "e": [ + 0, + 0 + ] }, + { "t": 21 }] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "fl", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { + "t": 17 + } + ] + }, + "o": { "a": 0, "k": 100 }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "a": 1, + "k": [ + { + "i": { "x": 0.833, "y": 0.833 }, + "o": { "x": 0.167, "y": 0.167 }, + "n": "0p833_0p833_0p167_0p167", + "t": 6, + "s": [ + 16.585, + -99.759 + ], + "e": [ + 28.521, + -187.495 + ], + "to": [ + 1.9892578125, + -14.6227216720581 + ], + "ti": [ + -1.9892578125, + 14.6227216720581 + ] + }, + { "t": 21 } + ], + "ix": 2 + }, + "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, + "s": { "a": 0, "k": [ + 97.419, + 116 + ], "ix": 3 }, + "r": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "n": ["0p833_0p833_0p167_0p167"], + "t": 6, + "s": [14.733], + "e": [8.733] + }, + { + "t": 21 + } + ], + "ix": 6 + }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 9, + "ty": 4, + "nm": "Shape Layer 5", + "parent": 11, + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0.8, + -0.641, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 7.39, + 7.39, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -6.977, + 7 + ], + [ + 6.973, + -7 + ] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -6.977, + 7 + ], + [ + 6.973, + -7 + ] + ], + "c": false + }, + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { "ty": "tr", "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "r": { "a": 0, "k": -90, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Group 1", + "np": 1, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { "ty": "mm", "mm": 2, "nm": "Merge Paths 1", "mn": "ADBE Vector Filter - Merge", "hd": false }, + { "ty": "st", "c": { "a": 0, "k": [ + 1, + 1, + 1, + 1 + ], "ix": 3 }, "o": { "a": 0, "k": 100, "ix": 4 }, "w": { "a": 0, "k": 4, "ix": 5 }, "lc": 2, "lj": 2, "bm": 0, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false }, + { "ty": "tr", "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 200, + 200 + ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Combined Shape", + "np": 4, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 7, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 10, + "ty": 4, + "nm": "Shape Layer 6", + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 4, + "s": [50], + "e": [0] + }, + { "t": 14 } + ] + }, + "r": { "a": 0, "k": 0 }, + "p": { "a": 0, "k": [ + 160, + 160, + 0 + ] }, + "a": { "a": 0, "k": [ + 0, + 0, + 0 + ] }, + "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667, + 0.667 + ], "y": [ + 1, + 1, + 0.667 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p667_1_0p333_0", + "0p667_1_0p333_0", + "0p667_0p667_0p333_0p333" + ], "t": 4, "s": [ + 100, + 100, + 100 + ], "e": [ + 1085, + 1085, + 100 + ] }, + { "t": 14 }] } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 0, "k": [ + 19.779, + 19.779 + ] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { "ty": "fl", "c": { "a": 0, "k": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] }, "o": { "a": 0, "k": 100 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill" }, + { "ty": "tr", "p": { "a": 0, "k": [ + -0.068, + 0.036 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + } + ], + "ip": 4, + "op": 22, + "st": -23, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 11, + "ty": 4, + "nm": "Shape Layer 4", + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 6, + "s": [30], + "e": [100] + }, + { "t": 9 } + ] + }, + "r": { "a": 0, "k": 0 }, + "p": { "a": 0, "k": [ + 160.312, + 161.188, + 0 + ] }, + "a": { "a": 0, "k": [ + 0.812, + -0.562, + 0 + ] }, + "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667, + 0.667 + ], "y": [ + 1, + 1, + 0.667 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p667_1_0p333_0", + "0p667_1_0p333_0", + "0p667_0p667_0p333_0p333" + ], "t": 6, "s": [ + 100, + 100, + 100 + ], "e": [ + 1087, + 1087, + 100 + ] }, + { "i": { "x": [ + 0.667, + 0.667, + 0.667 + ], "y": [ + 1, + 1, + 0.667 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p667_1_0p333_0", + "0p667_1_0p333_0", + "0p667_0p667_0p333_0p333" + ], "t": 11, "s": [ + 1087, + 1087, + 100 + ], "e": [ + 866, + 866, + 100 + ] }, + { "i": { "x": [ + 0.833, + 0.833, + 0.833 + ], "y": [ + 0.833, + 0.833, + 0.833 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p833_0p833_0p333_0", + "0p833_0p833_0p333_0", + "0p833_0p833_0p333_0p333" + ], "t": 13, "s": [ + 866, + 866, + 100 + ], "e": [ + 878, + 878, + 100 + ] }, + { "t": 16 }] } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 0, "k": [ + 10.068, + 10.068 + ] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { "ty": "fl", "c": { "a": 0, "k": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] }, "o": { "a": 0, "k": 100 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill" }, + { "ty": "tr", "p": { "a": 0, "k": [ + 0.784, + -0.716 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + } + ], + "ip": 6, + "op": 22, + "st": -19, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 12, + "ty": 4, + "nm": "Shape Layer 3", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 161, + 160, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + 0, + 0 + ] }, "s": { "a": 1, "k": [ + { "i": { "x": [ + 0.667, + 0.667, + 0.667 + ], "y": [ + 1, + 1, + 0.667 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p667_1_0p333_0", + "0p667_1_0p333_0", + "0p667_0p667_0p333_0p333" + ], "t": 3, "s": [ + 100, + 100, + 100 + ], "e": [ + 224, + 224, + 100 + ] }, + { "i": { "x": [ + 0.667, + 0.667, + 0.667 + ], "y": [ + 1, + 1, + 0.667 + ] }, "o": { "x": [ + 0.333, + 0.333, + 0.333 + ], "y": [ + 0, + 0, + 0.333 + ] }, "n": [ + "0p667_1_0p333_0", + "0p667_1_0p333_0", + "0p667_0p667_0p333_0p333" + ], "t": 4, "s": [ + 224, + 224, + 100 + ], "e": [ + 476, + 476, + 100 + ] }, + { "t": 8 }] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 0, "k": [ + 6.009, + 6.009 + ] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "st", + "c": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 4, + "s": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ], + "e": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] + }, + { "t": 8 } + ] + }, + "o": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 4, + "s": [0], + "e": [100] + }, + { "t": 5 } + ] + }, + "w": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 4, + "s": [3], + "e": [0] + }, + { "t": 8 } + ] + }, + "lc": 1, + "lj": 1, + "ml": 4, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke" + }, + { + "ty": "fl", + "c": { "a": 0, "k": [ + 0.8274509, + 0.1843137, + 0.1843137, + 1 + ] }, + "o": { + "a": 1, + "k": [ + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 3, + "s": [100], + "e": [99] + }, + { + "i": { "x": [0.667], "y": [1] }, + "o": { "x": [0.333], "y": [0] }, + "n": ["0p667_1_0p333_0"], + "t": 4, + "s": [99], + "e": [0] + }, + { "t": 5 } + ] + }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { "ty": "tr", "p": { "a": 0, "k": [ + -0.338, + 0.065 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 649.112, + 649.112 + ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Ellipse 2", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + } + ], + "ip": 3, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 13, + "ty": 4, + "nm": "Shape Layer 2", + "ks": { "o": { "a": 0, "k": 100 }, "r": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 160.142, + 159.987, + 0 + ] }, "a": { "a": 0, "k": [ + 0, + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 377.603, + 377.603, + 100 + ] } }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { "d": 1, "ty": "el", "s": { "a": 0, "k": [ + 22.315, + 22.315 + ] }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "nm": "Ellipse Path 1", "mn": "ADBE Vector Shape - Ellipse" }, + { + "ty": "st", + "c": { "a": 0, "k": [ + 0.8352941, + 0.8352941, + 0.8352941, + 1 + ] }, + "o": { + "a": 0, + "k": 100 + }, + "w": { "a": 0, "k": 1 }, + "lc": 1, + "lj": 1, + "ml": 4, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke" + }, + { "ty": "fl", "c": { "a": 0, "k": [ + 1, + 1, + 1, + 1 + ] }, "o": { "a": 0, "k": 100 }, "r": 1, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill" }, + { "ty": "tr", "p": { "a": 0, "k": [ + -0.038, + 0.003 + ], "ix": 2 }, "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "r": { "a": 0, "k": 0, "ix": 6 }, "o": { "a": 0, "k": 100, "ix": 7 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "nm": "Transform" } + ], + "nm": "Ellipse 1", + "np": 3, + "cix": 2, + "ix": 1, + "mn": "ADBE Vector Group" + } + ], + "ip": -21, + "op": 22, + "st": -21, + "bm": 0, + "sr": 1 + } + ] +} diff --git a/src/assets/animations/loading_animation.json b/src/assets/animations/loading_animation.json new file mode 100644 index 000000000..9255cda72 --- /dev/null +++ b/src/assets/animations/loading_animation.json @@ -0,0 +1,3168 @@ +{ + "nm": "Main Scene", + "ddd": 0, + "h": 500, + "w": 500, + "meta": { "g": "@lottiefiles/creator 1.47.1" }, + "layers": [ + { + "ty": 0, + "nm": "Nested Scene 1", + "sr": 1, + "st": 0, + "op": 92, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + 32.00010198974633, + 32.00128392491024 + ] }, "s": { "a": 0, "k": [ + 214.1158, + 214.1158 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 250, + 250 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } }, + "w": 500, + "h": 500, + "refId": "precomp_Shape Layer - SVG_9a65cf1e-7dbf-4e06-85a4-5dc45595fbad", + "ind": 1 + }, + { + "ty": 4, + "nm": "Shape Layer 5", + "sr": 1, + "st": 0, + "op": 387.000015762833, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + -17.052704811096184, + -15.0467209815979 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 362.228, + 362.228, + 100 + ], "ix": 6 }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 188.2303284168625, + 195.49656352277756 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 10 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100, "ix": 11 } }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Shape 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -2.003, + 0 + ], + [ + -0.231, + -6.654 + ], + [ + -4.034, + -2.519 + ], + [ + 0, + -4.262 + ], + [ + 3.615, + -2.257 + ], + [ + 0.165, + -4.753 + ], + [ + 6.655, + 0 + ], + [ + 1.814, + 0.965 + ], + [ + 2.33, + 0 + ], + [ + 2.057, + -1.094 + ], + [ + 2.003, + 0 + ], + [ + 0.231, + 6.654 + ], + [ + 4.034, + 2.519 + ], + [ + 0, + 4.262 + ], + [ + -3.615, + 2.257 + ], + [ + -0.165, + 4.753 + ], + [ + -6.655, + 0 + ], + [ + -1.814, + -0.965 + ], + [ + -2.33, + 0 + ], + [ + -2.057, + 1.094 + ] + ], + "o": [ + [ + 6.655, + 0 + ], + [ + 0.165, + 4.753 + ], + [ + 3.615, + 2.257 + ], + [ + 0, + 4.262 + ], + [ + -4.034, + 2.519 + ], + [ + -0.231, + 6.654 + ], + [ + -2.003, + 0 + ], + [ + -2.057, + -1.094 + ], + [ + -2.33, + 0 + ], + [ + -1.814, + 0.965 + ], + [ + -6.655, + 0 + ], + [ + -0.165, + -4.753 + ], + [ + -3.615, + -2.257 + ], + [ + 0, + -4.262 + ], + [ + 4.034, + -2.519 + ], + [ + 0.231, + -6.654 + ], + [ + 2.003, + 0 + ], + [ + 2.057, + 1.094 + ], + [ + 2.33, + 0 + ], + [ + 1.814, + -0.965 + ] + ], + "v": [ + [ + 12.457, + -33.889 + ], + [ + 24.737, + -22.024 + ], + [ + 31.441, + -10.411 + ], + [ + 37.214, + 0 + ], + [ + 31.442, + 10.411 + ], + [ + 24.737, + 22.024 + ], + [ + 12.457, + 33.889 + ], + [ + 6.705, + 32.435 + ], + [ + 0, + 30.762 + ], + [ + -6.705, + 32.435 + ], + [ + -12.457, + 33.889 + ], + [ + -24.737, + 22.024 + ], + [ + -31.441, + 10.411 + ], + [ + -37.214, + 0 + ], + [ + -31.441, + -10.411 + ], + [ + -24.737, + -22.024 + ], + [ + -12.457, + -33.889 + ], + [ + -6.705, + -32.434 + ], + [ + 0, + -30.762 + ], + [ + 6.705, + -32.434 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "gs", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - G-Stroke", + "nm": "Gradient Stroke 1", + "e": { "a": 0, "k": [ + 100, + 0 + ], "ix": 5 }, + "g": { "p": 3, "k": { "a": 0, "k": [ + 0, + 1, + 0.4470588235294118, + 0, + 0.485, + 0.9725490196078431, + 0.2235294117647059, + 0.5019607843137255, + 1, + 0.9411764705882353, + 0, + 1 + ], "ix": 8 } }, + "t": 1, + "a": { "a": 0, "k": 0 }, + "h": { "a": 0, "k": 0 }, + "s": { "a": 0, "k": [ + 0, + 0 + ], "ix": 4 }, + "lc": 2, + "lj": 1, + "ml": 4, + "o": { "a": 0, "k": 100, "ix": 9 }, + "w": { "a": 0, "k": 2, "ix": 10 } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { "a": 0, "k": [ + 0.0941, + 0.5294, + 0.6863 + ], "ix": 4 }, + "r": 1, + "o": { "a": 0, "k": 0, "ix": 5 } + }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 6 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "o": { "a": 0, "k": 100, "ix": 7 } } + ] + }, + { + "ty": "tm", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Filter - Trim", + "nm": "Trim Paths 1", + "ix": 2, + "e": { "a": 0, "k": 28, "ix": 2 }, + "o": { + "a": 1, + "k": [ + { "o": { "x": 0.167, "y": 0.167 }, "i": { "x": 0.833, "y": 0.833 }, "s": [0], "t": 0 }, + { "s": [360], "t": 90.0000036657751 } + ], + "ix": 3 + }, + "s": { "a": 0, "k": 0, "ix": 1 }, + "m": 1 + } + ], + "ind": 2 + }, + { + "ty": 4, + "nm": "Shape Layer 4", + "sr": 1, + "st": 0, + "op": 387.000015762833, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + 0, + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 362.228, + 362.228, + 100 + ], "ix": 6 }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 250, + 250, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 60, "ix": 10 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100, "ix": 11 } }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Shape 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -2.003, + 0 + ], + [ + -0.231, + -6.654 + ], + [ + -4.034, + -2.519 + ], + [ + 0, + -4.262 + ], + [ + 3.615, + -2.257 + ], + [ + 0.165, + -4.753 + ], + [ + 6.655, + 0 + ], + [ + 1.814, + 0.965 + ], + [ + 2.33, + 0 + ], + [ + 2.057, + -1.094 + ], + [ + 2.003, + 0 + ], + [ + 0.231, + 6.654 + ], + [ + 4.034, + 2.519 + ], + [ + 0, + 4.262 + ], + [ + -3.615, + 2.257 + ], + [ + -0.165, + 4.753 + ], + [ + -6.655, + 0 + ], + [ + -1.814, + -0.965 + ], + [ + -2.33, + 0 + ], + [ + -2.057, + 1.094 + ] + ], + "o": [ + [ + 6.655, + 0 + ], + [ + 0.165, + 4.753 + ], + [ + 3.615, + 2.257 + ], + [ + 0, + 4.262 + ], + [ + -4.034, + 2.519 + ], + [ + -0.231, + 6.654 + ], + [ + -2.003, + 0 + ], + [ + -2.057, + -1.094 + ], + [ + -2.33, + 0 + ], + [ + -1.814, + 0.965 + ], + [ + -6.655, + 0 + ], + [ + -0.165, + -4.753 + ], + [ + -3.615, + -2.257 + ], + [ + 0, + -4.262 + ], + [ + 4.034, + -2.519 + ], + [ + 0.231, + -6.654 + ], + [ + 2.003, + 0 + ], + [ + 2.057, + 1.094 + ], + [ + 2.33, + 0 + ], + [ + 1.814, + -0.965 + ] + ], + "v": [ + [ + 12.457, + -33.889 + ], + [ + 24.737, + -22.024 + ], + [ + 31.441, + -10.411 + ], + [ + 37.214, + 0 + ], + [ + 31.442, + 10.411 + ], + [ + 24.737, + 22.024 + ], + [ + 12.457, + 33.889 + ], + [ + 6.705, + 32.435 + ], + [ + 0, + 30.762 + ], + [ + -6.705, + 32.435 + ], + [ + -12.457, + 33.889 + ], + [ + -24.737, + 22.024 + ], + [ + -31.441, + 10.411 + ], + [ + -37.214, + 0 + ], + [ + -31.441, + -10.411 + ], + [ + -24.737, + -22.024 + ], + [ + -12.457, + -33.889 + ], + [ + -6.705, + -32.434 + ], + [ + 0, + -30.762 + ], + [ + 6.705, + -32.434 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "gs", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - G-Stroke", + "nm": "Gradient Stroke 1", + "e": { "a": 0, "k": [ + 100, + 0 + ], "ix": 5 }, + "g": { "p": 3, "k": { "a": 0, "k": [ + 0, + 0, + 0.7764705882352941, + 1, + 0.485, + 0.24705882352941178, + 0.38823529411764707, + 1, + 1, + 0.49411764705882355, + 0, + 1 + ], "ix": 8 } }, + "t": 1, + "a": { "a": 0, "k": 0 }, + "h": { "a": 0, "k": 0 }, + "s": { "a": 0, "k": [ + 0, + 0 + ], "ix": 4 }, + "lc": 2, + "lj": 1, + "ml": 4, + "o": { "a": 0, "k": 100, "ix": 9 }, + "w": { "a": 0, "k": 0.5, "ix": 10 } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { "a": 0, "k": [ + 0.4118, + 0.6784, + 0.7765 + ], "ix": 4 }, + "r": 1, + "o": { "a": 0, "k": 0, "ix": 5 } + }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 6 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "o": { "a": 0, "k": 100, "ix": 7 } } + ] + }, + { + "ty": "tm", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Filter - Trim", + "nm": "Trim Paths 1", + "ix": 2, + "e": { "a": 0, "k": 28, "ix": 2 }, + "o": { + "a": 1, + "k": [ + { "o": { "x": 0.167, "y": 0.167 }, "i": { "x": 0.833, "y": 0.833 }, "s": [0], "t": 0 }, + { "s": [360], "t": 90.0000036657751 } + ], + "ix": 3 + }, + "s": { "a": 0, "k": 0, "ix": 1 }, + "m": 1 + } + ], + "ind": 3 + }, + { + "ty": 4, + "nm": "Shape Layer 3", + "sr": 1, + "st": 0, + "op": 387.000015762833, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + 0, + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 362.228, + 362.228, + 100 + ], "ix": 6 }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 250, + 250, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 30, "ix": 10 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100, "ix": 11 } }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Shape 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -2.003, + 0 + ], + [ + -0.231, + -6.654 + ], + [ + -4.034, + -2.519 + ], + [ + 0, + -4.262 + ], + [ + 3.615, + -2.257 + ], + [ + 0.165, + -4.753 + ], + [ + 6.655, + 0 + ], + [ + 1.814, + 0.965 + ], + [ + 2.33, + 0 + ], + [ + 2.057, + -1.094 + ], + [ + 2.003, + 0 + ], + [ + 0.231, + 6.654 + ], + [ + 4.034, + 2.519 + ], + [ + 0, + 4.262 + ], + [ + -3.615, + 2.257 + ], + [ + -0.165, + 4.753 + ], + [ + -6.655, + 0 + ], + [ + -1.814, + -0.965 + ], + [ + -2.33, + 0 + ], + [ + -2.057, + 1.094 + ] + ], + "o": [ + [ + 6.655, + 0 + ], + [ + 0.165, + 4.753 + ], + [ + 3.615, + 2.257 + ], + [ + 0, + 4.262 + ], + [ + -4.034, + 2.519 + ], + [ + -0.231, + 6.654 + ], + [ + -2.003, + 0 + ], + [ + -2.057, + -1.094 + ], + [ + -2.33, + 0 + ], + [ + -1.814, + 0.965 + ], + [ + -6.655, + 0 + ], + [ + -0.165, + -4.753 + ], + [ + -3.615, + -2.257 + ], + [ + 0, + -4.262 + ], + [ + 4.034, + -2.519 + ], + [ + 0.231, + -6.654 + ], + [ + 2.003, + 0 + ], + [ + 2.057, + 1.094 + ], + [ + 2.33, + 0 + ], + [ + 1.814, + -0.965 + ] + ], + "v": [ + [ + 12.457, + -33.889 + ], + [ + 24.737, + -22.024 + ], + [ + 31.441, + -10.411 + ], + [ + 37.214, + 0 + ], + [ + 31.442, + 10.411 + ], + [ + 24.737, + 22.024 + ], + [ + 12.457, + 33.889 + ], + [ + 6.705, + 32.435 + ], + [ + 0, + 30.762 + ], + [ + -6.705, + 32.435 + ], + [ + -12.457, + 33.889 + ], + [ + -24.737, + 22.024 + ], + [ + -31.441, + 10.411 + ], + [ + -37.214, + 0 + ], + [ + -31.441, + -10.411 + ], + [ + -24.737, + -22.024 + ], + [ + -12.457, + -33.889 + ], + [ + -6.705, + -32.434 + ], + [ + 0, + -30.762 + ], + [ + 6.705, + -32.434 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "gs", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - G-Stroke", + "nm": "Gradient Stroke 1", + "e": { "a": 0, "k": [ + 100, + 0 + ], "ix": 5 }, + "g": { "p": 3, "k": { "a": 0, "k": [ + 0, + 1, + 0.8235294117647058, + 0, + 0.485, + 0.5019607843137255, + 0.8117647058823529, + 0.5019607843137255, + 1, + 0, + 0.8, + 1 + ], "ix": 8 } }, + "t": 1, + "a": { "a": 0, "k": 0 }, + "h": { "a": 0, "k": 0 }, + "s": { "a": 0, "k": [ + 0, + 0 + ], "ix": 4 }, + "lc": 2, + "lj": 1, + "ml": 4, + "o": { "a": 0, "k": 100, "ix": 9 }, + "w": { "a": 0, "k": 0.5, "ix": 10 } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { "a": 0, "k": [ + 0.4118, + 0.6784, + 0.7765 + ], "ix": 4 }, + "r": 1, + "o": { "a": 0, "k": 0, "ix": 5 } + }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 6 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "o": { "a": 0, "k": 100, "ix": 7 } } + ] + }, + { + "ty": "tm", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Filter - Trim", + "nm": "Trim Paths 1", + "ix": 2, + "e": { "a": 0, "k": 28, "ix": 2 }, + "o": { + "a": 1, + "k": [ + { "o": { "x": 0.167, "y": 0.167 }, "i": { "x": 0.833, "y": 0.833 }, "s": [0], "t": 0 }, + { "s": [360], "t": 90.0000036657751 } + ], + "ix": 3 + }, + "s": { "a": 0, "k": 0, "ix": 1 }, + "m": 1 + } + ], + "ind": 4 + }, + { + "ty": 4, + "nm": "Shape Layer 1", + "sr": 1, + "st": 0, + "op": 387.000015762833, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + 0, + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 362.228, + 362.228, + 100 + ], "ix": 6 }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 250, + 250, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 10 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 8, "ix": 11 } }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Group", + "nm": "Shape 1", + "ix": 1, + "cix": 2, + "np": 3, + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Shape - Group", + "nm": "Path 1", + "ix": 1, + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + -2.003, + 0 + ], + [ + -0.231, + -6.654 + ], + [ + -4.034, + -2.519 + ], + [ + 0, + -4.262 + ], + [ + 3.615, + -2.257 + ], + [ + 0.165, + -4.753 + ], + [ + 6.655, + 0 + ], + [ + 1.814, + 0.965 + ], + [ + 2.33, + 0 + ], + [ + 2.057, + -1.094 + ], + [ + 2.003, + 0 + ], + [ + 0.231, + 6.654 + ], + [ + 4.034, + 2.519 + ], + [ + 0, + 4.262 + ], + [ + -3.615, + 2.257 + ], + [ + -0.165, + 4.753 + ], + [ + -6.655, + 0 + ], + [ + -1.814, + -0.965 + ], + [ + -2.33, + 0 + ], + [ + -2.057, + 1.094 + ] + ], + "o": [ + [ + 6.655, + 0 + ], + [ + 0.165, + 4.753 + ], + [ + 3.615, + 2.257 + ], + [ + 0, + 4.262 + ], + [ + -4.034, + 2.519 + ], + [ + -0.231, + 6.654 + ], + [ + -2.003, + 0 + ], + [ + -2.057, + -1.094 + ], + [ + -2.33, + 0 + ], + [ + -1.814, + 0.965 + ], + [ + -6.655, + 0 + ], + [ + -0.165, + -4.753 + ], + [ + -3.615, + -2.257 + ], + [ + 0, + -4.262 + ], + [ + 4.034, + -2.519 + ], + [ + 0.231, + -6.654 + ], + [ + 2.003, + 0 + ], + [ + 2.057, + 1.094 + ], + [ + 2.33, + 0 + ], + [ + 1.814, + -0.965 + ] + ], + "v": [ + [ + 12.457, + -33.889 + ], + [ + 24.737, + -22.024 + ], + [ + 31.441, + -10.411 + ], + [ + 37.214, + 0 + ], + [ + 31.442, + 10.411 + ], + [ + 24.737, + 22.024 + ], + [ + 12.457, + 33.889 + ], + [ + 6.705, + 32.435 + ], + [ + 0, + 30.762 + ], + [ + -6.705, + 32.435 + ], + [ + -12.457, + 33.889 + ], + [ + -24.737, + 22.024 + ], + [ + -31.441, + 10.411 + ], + [ + -37.214, + 0 + ], + [ + -31.441, + -10.411 + ], + [ + -24.737, + -22.024 + ], + [ + -12.457, + -33.889 + ], + [ + -6.705, + -32.434 + ], + [ + 0, + -30.762 + ], + [ + 6.705, + -32.434 + ] + ] + }, + "ix": 2 + } + }, + { + "ty": "st", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Stroke", + "nm": "Stroke 1", + "lc": 2, + "lj": 1, + "ml": 4, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 4, "ix": 5 }, + "c": { "a": 0, "k": [ + 0.8157, + 0.8157, + 0.8157 + ], "ix": 3 } + }, + { + "ty": "fl", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Graphic - Fill", + "nm": "Fill 1", + "c": { "a": 0, "k": [ + 0.4118, + 0.6784, + 0.7765 + ], "ix": 4 }, + "r": 1, + "o": { "a": 0, "k": 0, "ix": 5 } + }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ], "ix": 1 }, "s": { "a": 0, "k": [ + 100, + 100 + ], "ix": 3 }, "sk": { "a": 0, "k": 0, "ix": 4 }, "p": { "a": 0, "k": [ + 0, + 0 + ], "ix": 2 }, "r": { "a": 0, "k": 0, "ix": 6 }, "sa": { "a": 0, "k": 0, "ix": 5 }, "o": { "a": 0, "k": 100, "ix": 7 } } + ] + }, + { + "ty": "tm", + "bm": 0, + "hd": false, + "mn": "ADBE Vector Filter - Trim", + "nm": "Trim Paths 1", + "ix": 2, + "e": { "a": 0, "k": 100, "ix": 2 }, + "o": { "a": 0, "k": 0, "ix": 3 }, + "s": { "a": 0, "k": 0, "ix": 1 }, + "m": 1 + } + ], + "ind": 5 + } + ], + "v": "5.7.0", + "fr": 29.9700012207031, + "op": 92, + "ip": 0, + "assets": [ + { + "nm": "Nested Scene 1", + "id": "precomp_Shape Layer - SVG_9a65cf1e-7dbf-4e06-85a4-5dc45595fbad", + "fr": 29.9700012207031, + "layers": [ + { + "ty": 4, + "nm": "Shape Layer - SVG", + "sr": 1, + "st": 0, + "op": 92, + "ip": 0, + "hd": false, + "ddd": 0, + "bm": 0, + "hasMask": false, + "ao": 0, + "ks": { "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } }, + "shapes": [ + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 1", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 1", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + -0.4866000000000028, + -2.1432 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -3.7177000000000007, + 4.1865999999999985 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 1.5194000000000045, + 0.4987999999999957 + ], + [ + -0.10560000000000258, + -0.7130999999999972 + ] + ], + "o": [ + [ + 0.36659999999999826, + 2.4802999999999997 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 3.657899999999998, + -0.33209999999999695 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -4.331400000000002, + 0.12060000000000315 + ], + [ + -0.2796999999999983, + -0.091700000000003 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 37.8934, + 52.3959 + ], + [ + 39.3607, + 60.3476 + ], + [ + 39.9165, + 62.7956 + ], + [ + 40.4902, + 62.7433 + ], + [ + 55.8807, + 53.3144 + ], + [ + 56.5203, + 52.5945 + ], + [ + 53.2122, + 52.6864 + ], + [ + 38.086, + 51.7085 + ], + [ + 37.8934, + 52.3959 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.4627, + 0.7686, + 0.4784 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 2", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 2", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + -0.15738999999999947, + -0.445600000000006 + ], + [ + -3.0069, + -1.603900000000003 + ], + [ + -5.0671, + 0.9211000000000027 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0.38759999999999906, + 2.5437999999999974 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 5.086, + 3.870999999999995 + ], + [ + 0.07519999999999882, + 0 + ], + [ + 0.6423999999999985, + -2.1193000000000026 + ] + ], + "o": [ + [ + -1.3162400000000005, + 4.343699999999998 + ], + [ + 0.38225000000000087, + 1.083199999999998 + ], + [ + 5.5031, + 2.9361999999999995 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -0.5576000000000008, + -2.1340000000000003 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -6.5581, + -2.023600000000002 + ], + [ + -0.9616000000000007, + -0.7317999999999998 + ], + [ + -0.07540000000000013, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 10.3213, + 43.5292 + ], + [ + 7.99771, + 53.13 + ], + [ + 16.8523, + 60.1514 + ], + [ + 35.6616, + 63.737 + ], + [ + 36.4296, + 63.5977 + ], + [ + 36.1118, + 62.3806 + ], + [ + 34.2685, + 53.2599 + ], + [ + 33.8928, + 50.7962 + ], + [ + 31.2208, + 49.9716 + ], + [ + 13.5113, + 41.0065 + ], + [ + 11.6262, + 39.6758 + ], + [ + 10.3213, + 43.5292 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.0275, + 0.451, + 0.7333 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 3", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 3", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + -3.384233, + -5.6932000000000045 + ], + [ + -0.3778100000000002, + 1.6189999999999998 + ], + [ + -0.9020400000000004, + 2.518900000000002 + ], + [ + 0.00140000000000029, + 0.06499999999999773 + ], + [ + 0.8331499999999998, + 0.9473999999999947 + ], + [ + 1.5369500000000003, + 2.5269000000000013 + ], + [ + 0.06304700000000008, + 0.08149999999999835 + ], + [ + 0.10811399999999999, + -0.7799000000000014 + ] + ], + "o": [ + [ + -0.9809669999999999, + 7.0760000000000005 + ], + [ + 0.6531199999999995, + 1.0985000000000014 + ], + [ + 0.51349, + -2.1987000000000023 + ], + [ + 0.4948000000000006, + -1.3810000000000002 + ], + [ + -0.0014099999999999113, + -0.06500000000000483 + ], + [ + -2.0102700000000002, + -2.286300000000004 + ], + [ + -0.6974799999999999, + -1.1469999999999985 + ], + [ + -0.06281399999999993, + -0.08170000000000144 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 0.299989, + 27.289 + ], + [ + 4.16338, + 47.8151 + ], + [ + 5.20085, + 47.2916 + ], + [ + 7.55905, + 39.4327 + ], + [ + 8.45619, + 36.8034 + ], + [ + 6.93862, + 34.9626 + ], + [ + 1.99385, + 28.2532 + ], + [ + 0.611022, + 26.0196 + ], + [ + 0.299989, + 27.289 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.1333, + 0.698, + 0.298 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 4", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 4", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + -0.5378000000000043, + -0.5847000000000051 + ], + [ + -2.363100000000003, + -0.1734000000000009 + ], + [ + -0.9472999999999985, + 0.6635999999999953 + ], + [ + 0, + 5.052500000000002 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 7.270699999999998, + 2.3749000000000002 + ], + [ + 0.05420000000000158, + -0.2012999999999998 + ] + ], + "o": [ + [ + -0.5215999999999994, + 1.9314 + ], + [ + 0.12959999999999638, + 0.14089999999999492 + ], + [ + 4.624599999999994, + 0.33910000000000196 + ], + [ + 1.7532000000000068, + -1.2280000000000015 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -6.882399999999997, + 1.0878000000000014 + ], + [ + -0.17260000000000275, + -0.056400000000000006 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 37.2526, + 29.286 + ], + [ + 37.3708, + 47.7764 + ], + [ + 45.1527, + 48.7571 + ], + [ + 59.6112, + 47.9155 + ], + [ + 64, + 32.1945 + ], + [ + 64, + 31.0465 + ], + [ + 62.3739, + 31.3035 + ], + [ + 37.6188, + 29.0519 + ], + [ + 37.2526, + 29.286 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.1569, + 0.6745, + 0.8863 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 5", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 5", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + 1.3225999999999996, + -3.450000000000003 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -5.736600000000003, + -1.8301000000000016 + ], + [ + -0.17549999999999955, + -0.05740000000000123 + ], + [ + -0.17549999999999955, + 0.1676000000000002 + ], + [ + 0.06490000000000151, + 0.7081000000000017 + ], + [ + 0, + 0 + ], + [ + 0.01559999999999917, + 0.19009999999999394 + ], + [ + -0.46280000000000143, + 2.0993999999999993 + ], + [ + 1.0622000000000007, + 0.5338999999999992 + ], + [ + 2.0352999999999994, + 1.5974000000000004 + ], + [ + 0.1661999999999999, + 0.1280000000000001 + ], + [ + 1.0383999999999993, + -1.343 + ] + ], + "o": [ + [ + -2.991699999999998, + 3.869200000000003 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 4.527899999999999, + 3.899700000000003 + ], + [ + 0.2134999999999998, + 0.06810000000000116 + ], + [ + 0.9068999999999967, + 0.2961999999999989 + ], + [ + 0.15290000000000248, + -0.14610000000000412 + ], + [ + 0, + 0 + ], + [ + -0.014400000000001967, + -0.15669999999999362 + ], + [ + -0.3248000000000033, + -3.954800000000006 + ], + [ + 0.017600000000001614, + -0.07900000000000063 + ], + [ + -2.2921000000000014, + -1.1525999999999996 + ], + [ + -0.8074000000000012, + -0.6334000000000017 + ], + [ + -0.27069999999999794, + -0.2079000000000022 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 21.1862, + 21.7642 + ], + [ + 13.3941, + 34.984 + ], + [ + 13.1426, + 35.6405 + ], + [ + 14.5119, + 36.82 + ], + [ + 31.2098, + 46.141 + ], + [ + 31.7925, + 46.3298 + ], + [ + 33.2972, + 46.5964 + ], + [ + 33.3505, + 45.364 + ], + [ + 33.3504, + 45.3627 + ], + [ + 33.3046, + 44.843 + ], + [ + 33.6959, + 27.6811 + ], + [ + 31.7966, + 26.5666 + ], + [ + 24.4205, + 21.8794 + ], + [ + 22.6501, + 20.4948 + ], + [ + 21.1862, + 21.7642 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.0275, + 0.451, + 0.7333 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 6", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 6", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + 1.6971300000000005, + -3.8272999999999993 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -2.9274900000000006, + -3.475900000000003 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -2.8207000000000004, + 3.688200000000002 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 1.5810999999999993, + 2.36097 + ], + [ + 0.11470000000000091, + 0.17483000000000004 + ], + [ + 0.2865000000000002, + 0.03232999999999997 + ], + [ + 0.6156000000000006, + -0.6015199999999998 + ], + [ + 0.1765699999999999, + -0.16886000000000045 + ] + ], + "o": [ + [ + -2.967649999999999, + 2.8382400000000008 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 2.05276, + 3.5570999999999984 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 1.872300000000001, + -3.720200000000002 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -1.805299999999999, + -1.9300999999999995 + ], + [ + -0.14390000000000036, + -0.21494999999999997 + ], + [ + -0.5118999999999989, + -0.7804499999999992 + ], + [ + -0.27949999999999875, + -0.03155000000000019 + ], + [ + -0.14119999999999955, + 0.13804999999999978 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 9.77953, + 8.90166 + ], + [ + 2.72246, + 18.985 + ], + [ + 1.80664, + 21.05 + ], + [ + 2.237, + 21.7956 + ], + [ + 9.2392, + 31.6839 + ], + [ + 10.2195, + 32.8482 + ], + [ + 11.2128, + 30.8746 + ], + [ + 18.2271, + 19.8019 + ], + [ + 19.7674, + 17.788 + ], + [ + 18.4287, + 16.3567 + ], + [ + 12.8987, + 9.34923 + ], + [ + 12.5124, + 8.7648 + ], + [ + 11.4613, + 7.56662 + ], + [ + 10.2546, + 8.44107 + ], + [ + 9.77953, + 8.90166 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.1569, + 0.6745, + 0.8863 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 7", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 7", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + 1.3486999999999938, + -6.994300000000001 + ], + [ + 0.03300000000000125, + -0.1656000000000013 + ], + [ + -0.17560000000000286, + -0.32069999999999865 + ], + [ + -1.0641999999999996, + -0.28910000000000124 + ], + [ + -0.1992000000000047, + -0.05529999999999902 + ], + [ + -6.824000000000005, + 0.6976000000000013 + ], + [ + -0.19290000000000163, + 0.01770000000000138 + ], + [ + -0.12379999999999569, + 0.20589999999999975 + ], + [ + 0.113900000000001, + 0.5217999999999989 + ], + [ + 0.038699999999998624, + 0.19280000000000186 + ], + [ + 8.160399999999996, + 4.55716 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + -2.9772999999999996, + 4.436579999999999 + ], + [ + -0.03750000000000142, + 0.1944999999999979 + ], + [ + -0.1910000000000025, + 0.9592999999999989 + ], + [ + 0.1897999999999982, + 0.34690000000000154 + ], + [ + 0.17009999999999792, + 0.04619999999999891 + ], + [ + 5.970399999999998, + 1.6580000000000013 + ], + [ + 0.2458999999999989, + -0.025099999999998346 + ], + [ + 0.811399999999999, + -0.07430000000000092 + ], + [ + 0.10180000000000433, + -0.16949999999999932 + ], + [ + -0.03289999999999793, + -0.15060000000000073 + ], + [ + -1.8084999999999951, + -8.997599999999998 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 45.3084, + 3.81117 + ], + [ + 38.2214, + 22.5377 + ], + [ + 38.1152, + 23.0771 + ], + [ + 38.0005, + 24.8556 + ], + [ + 39.7658, + 25.6305 + ], + [ + 40.3192, + 25.7821 + ], + [ + 61.5626, + 27.3769 + ], + [ + 62.2194, + 27.3141 + ], + [ + 63.5209, + 27.0004 + ], + [ + 63.4463, + 26.0229 + ], + [ + 63.3375, + 25.5092 + ], + [ + 47.4917, + 3.96349 + ], + [ + 45.8291, + 3.0351 + ], + [ + 45.3084, + 3.81117 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.1333, + 0.698, + 0.298 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 8", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 8", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + 1.8596000000000004, + -2.0977000000000015 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -0.2785999999999973, + 0.34830000000000183 + ], + [ + -0.30169999999999675, + 1.3917000000000002 + ], + [ + -2.1699999999999946, + 4.5597199999999996 + ], + [ + 0.06470000000000198, + -0.0043199999999998795 + ], + [ + 1.8033999999999963, + -1.3320400000000001 + ] + ], + "o": [ + [ + -4.951500000000003, + 3.65771 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 2.5614999999999988, + 2.0771000000000015 + ], + [ + 0.06560000000000343, + -0.08200000000000074 + ], + [ + 1.0449000000000055, + -4.822699999999999 + ], + [ + 0.7236000000000047, + -1.5209200000000003 + ], + [ + -0.06459999999999866, + 0.0043199999999998795 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 37.4538, + 6.75399 + ], + [ + 25.594, + 16.7754 + ], + [ + 24.9658, + 17.4839 + ], + [ + 26.1738, + 18.4636 + ], + [ + 34.3064, + 23.4148 + ], + [ + 34.974, + 20.7352 + ], + [ + 39.6523, + 7.08148 + ], + [ + 40.8504, + 4.32423 + ], + [ + 37.4538, + 6.75399 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.0235, + 0.451, + 0.7294 + ] }, "r": 1, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + }, + { + "ty": "gr", + "bm": 0, + "hd": false, + "nm": "Group 9", + "it": [ + { + "ty": "sh", + "bm": 0, + "hd": false, + "nm": "Path 9", + "d": 1, + "ks": { + "a": 0, + "k": { + "c": true, + "i": [ + [ + 0, + 0 + ], + [ + 0, + -1.0842699999999996 + ], + [ + -2.1457000000000015, + -2.427200000000001 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -5.013399999999997, + 3.6241999999999996 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 3.0864999999999974, + -0.32317300000000004 + ] + ], + "o": [ + [ + -4.299600000000002, + 0.450614 + ], + [ + 0, + 0.5206800000000005 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 3.7058, + -3.7046599999999987 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -2.9074000000000026, + -0.5727236099999999 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 28.4216, + 0.166472 + ], + [ + 14.792, + 5.03107 + ], + [ + 20.7359, + 13.1972 + ], + [ + 22.26, + 14.9212 + ], + [ + 23.9051, + 13.2769 + ], + [ + 36.2065, + 2.93718 + ], + [ + 39.1905, + 0.780068 + ], + [ + 38.1246, + 0.57012 + ], + [ + 28.4216, + 0.166472 + ] + ] + } + } + }, + { "ty": "fl", "bm": 0, "hd": false, "nm": "Fill", "c": { "a": 0, "k": [ + 0.4627, + 0.7686, + 0.4784 + ] }, "r": 2, "o": { "a": 0, "k": 100 } }, + { "ty": "tr", "a": { "a": 0, "k": [ + 0, + 0 + ] }, "s": { "a": 0, "k": [ + 100, + 100 + ] }, "sk": { "a": 0, "k": 0 }, "p": { "a": 0, "k": [ + 0, + 0 + ] }, "r": { "a": 0, "k": 0 }, "sa": { "a": 0, "k": 0 }, "o": { "a": 0, "k": 100 } } + ] + } + ], + "ind": 1 + } + ] + } + ] +} diff --git a/src/assets/env.js b/src/assets/env.js index e278f8d6f..934ea9a63 100644 --- a/src/assets/env.js +++ b/src/assets/env.js @@ -21,7 +21,7 @@ // in the tenants table of the tenant-store database, or the platform rejects the request as an // unknown tenant. window["env"]["fineractPlatformTenantId"] = 'utkarshbank'; - window["env"]["fineractPlatformTenantIds"] = 'utkarshbank,lincpay'; + window["env"]["fineractPlatformTenantIds"] = 'utkarshbank,lincpay,utkarshnagari,testbank,humarwork,default,mybank'; window['env']['tenantLogoUrl'] = ''; window['env']['tenantLogoUrlDark'] = ''; diff --git a/src/assets/images/mifos_splash_screen_logo.png b/src/assets/images/mifos_splash_screen_logo.png new file mode 100644 index 000000000..4a515337a Binary files /dev/null and b/src/assets/images/mifos_splash_screen_logo.png differ diff --git a/src/assets/styles/_loader.scss b/src/assets/styles/_loader.scss index c5c410894..ff3e124e9 100644 --- a/src/assets/styles/_loader.scss +++ b/src/assets/styles/_loader.scss @@ -6,53 +6,95 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* ========================================================================== + Modernized Loader Wrapper (Inspired by openMF/mifos-mobile) + Supports full-screen and embedded containers with smooth vector physics. + ========================================================================== */ .loader-wrapper { - height: 100vh; - width: 100vw; + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 280px; + height: 100%; + background: transparent; + /* Modern orbital rings that replace legacy rigid triangles smoothly */ .triangle { - width: 0; - height: 0; - border-style: solid; position: absolute; - animation: rotate 0.8s infinite 0.2s; + border-radius: 50%; + border-style: solid; + box-sizing: border-box; + transition: all 0.3s ease; } .bottom { - border-width: 0 90px 90px; - border-color: transparent transparent #1fb050; - bottom: calc(50% - 100px); - left: calc(50% - 90px); + width: 72px; + height: 72px; + border-width: 3px; + border-color: #2563eb transparent transparent; + animation: cbs-spin-ring 1.1s cubic-bezier(0.5, 0, 0.5, 1) infinite; + box-shadow: 0 0 16px rgb(37 99 235 / 35%); } .top { - border-width: 90px 90px 0; - border-color: #b4d575 transparent transparent; - top: calc(50% - 100px); - left: calc(50% - 90px); + width: 54px; + height: 54px; + border-width: 3px; + border-color: transparent transparent #0ea5e9; + animation: cbs-spin-ring 1.4s cubic-bezier(0.5, 0, 0.5, 1) infinite reverse; + box-shadow: 0 0 14px rgb(14 165 233 / 35%); } .left { - border-width: 90px 0 90px 90px; - border-color: transparent transparent transparent #1074b9; - top: calc(50% - 90px); - left: calc(50% - 100px); + width: 36px; + height: 36px; + border-width: 2.5px; + border-color: #10b981 transparent transparent; + animation: cbs-spin-ring 0.9s cubic-bezier(0.5, 0, 0.5, 1) infinite; + box-shadow: 0 0 10px rgb(16 185 129 / 35%); } .right { - border-width: 90px 90px 90px 0; - border-color: transparent #1daeec transparent transparent; - top: calc(50% - 90px); - right: calc(50% - 100px); + width: 18px; + height: 18px; + border-width: 2px; + border-color: transparent #38bdf8 transparent transparent; + animation: cbs-spin-ring 0.7s cubic-bezier(0.5, 0, 0.5, 1) infinite reverse; } - @keyframes rotate { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } + /* Optional central glowing core */ + &::after { + content: ''; + width: 8px; + height: 8px; + border-radius: 50%; + background: #38bdf8; + box-shadow: 0 0 12px #38bdf8; + animation: cbs-pulse-core 1.5s ease-in-out infinite; + } +} + +@keyframes cbs-spin-ring { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +@keyframes cbs-pulse-core { + 0%, + 100% { + transform: scale(0.85); + opacity: 0.6; + } + + 50% { + transform: scale(1.3); + opacity: 1; } } diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 7e1be2f0b..107944c83 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -20,7 +20,8 @@ export const environment = { hash: env.mifos_x.hash, // For connecting to server running elsewhere update the tenant identifier fineractPlatformTenantId: loadedEnv['fineractPlatformTenantId'] || 'utkarshbank', - fineractPlatformTenantIds: loadedEnv['fineractPlatformTenantIds'] || 'utkarshbank,lincpay', + fineractPlatformTenantIds: + loadedEnv['fineractPlatformTenantIds'] || 'utkarshbank,lincpay,utkarshnagari,testbank,humarwork,default,mybank', // For connecting to others servers running elsewhere update the base API URL baseApiUrls: loadedEnv['fineractApiUrls'] || 'https://demo.mifos.community,https://localhost:8443,' + window.location.origin, diff --git a/src/environments/environment.ts b/src/environments/environment.ts index dabb366fb..6db2d3da6 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -23,7 +23,8 @@ export const environment = { hash: env.mifos_x.hash, // For connecting to server running elsewhere update the tenant identifier fineractPlatformTenantId: loadedEnv.fineractPlatformTenantId || 'utkarshbank', - fineractPlatformTenantIds: loadedEnv.fineractPlatformTenantIds || 'utkarshbank,lincpay', + fineractPlatformTenantIds: + loadedEnv.fineractPlatformTenantIds || 'utkarshbank,lincpay,utkarshnagari,testbank,humarwork,default,mybank', // For connecting to others servers running elsewhere update the base API URL baseApiUrls: loadedEnv.fineractApiUrls || 'https://demo.mifos.community,https://localhost:8443,' + window.location.origin, diff --git a/src/index.html b/src/index.html index 129b49a38..b63e3f055 100644 --- a/src/index.html +++ b/src/index.html @@ -11,7 +11,7 @@ - {{'APP_NAME' | translate}} + CBS Nexora @@ -27,7 +27,10 @@ rel="stylesheet" /> - + - -
-
-
-
-
+
+ +
+
+ CBS Nexora + +
+ Next-Generation Core Banking
- + +
+
+
+
+
+
+
+ +
+
+
+ +

Connecting to Secure Banking Gateway...

+
+ + + + diff --git a/src/main.scss b/src/main.scss index c74b84479..a82ed67a8 100644 --- a/src/main.scss +++ b/src/main.scss @@ -81,66 +81,332 @@ // ===== Sub-menu Mega Panels (rendered in CDK overlay) ===== .deposits-mega-panel, -.hrm-mega-panel { - max-width: 700px !important; +.hrm-mega-panel, +.cashier-mega-panel, +.clerk-mega-panel, +.clearing-mega-panel, +.npa-mega-panel, +.advances-mega-panel, +.admin-mega-panel { + min-width: 680px !important; + max-width: 740px !important; +} + +.branch-ops-mega-panel, +.insurance-mega-panel, +.digital-mega-panel, +.ckyc-mega-panel { + min-width: 440px !important; + max-width: 480px !important; + overflow-x: hidden !important; } .share-mega-panel, .others-mega-panel { - max-width: 850px !important; + min-width: 780px !important; + max-width: 840px !important; } -.share-grid, -.others-grid { - grid-template-columns: repeat(4, 1fr) !important; -} - -.others-grid, -.npa-grid { - grid-template-columns: repeat(3, 1fr) !important; -} - -.npa-mega-panel, -.advances-mega-panel { +.reports-mega-panel { + min-width: 740px !important; max-width: 800px !important; + border-top: 2.5px solid #0369a1 !important; } -.advances-grid { - grid-template-columns: repeat(3, 1fr) !important; +.letter-mega-panel, +.reconciliation-mega-panel { + min-width: 900px !important; + max-width: 960px !important; +} + +/* ========================================================================== + Mega Menu Panel CDK Overlay Rules (Anchor naturally under triggers) + ========================================================================== */ +.cdk-overlay-pane:has(.mat-mdc-menu-panel) { + max-width: 96vw !important; +} + +/* Individual Top Accent Border for Mega Panels */ +.deposits-mega-panel, +.advances-mega-panel { + border-top: 2.5px solid #1074b9 !important; +} + +.cashier-mega-panel { + border-top: 2.5px solid #0284c7 !important; +} + +.clerk-mega-panel { + border-top: 2.5px solid #2563eb !important; +} + +.branch-ops-mega-panel { + border-top: 2.5px solid #4f46e5 !important; +} + +.clearing-mega-panel, +.reconciliation-mega-panel { + border-top: 2.5px solid #0d9488 !important; +} + +.hrm-mega-panel { + border-top: 2.5px solid #0284c7 !important; +} + +.admin-mega-panel, +.digital-mega-panel { + border-top: 2.5px solid #7c3aed !important; +} + +.share-mega-panel { + border-top: 2.5px solid #d97706 !important; +} + +.others-mega-panel { + border-top: 2.5px solid #0284c7 !important; +} + +.npa-mega-panel { + border-top: 2.5px solid #dc2626 !important; } .letter-mega-panel { - max-width: 1100px !important; + border-top: 2.5px solid #15803d !important; } -.letter-grid { - grid-template-columns: repeat(5, minmax(190px, 1fr)) !important; - min-width: 1040px; +.insurance-mega-panel { + border-top: 2.5px solid #c2410c !important; } -.reconciliation-mega-panel { - max-width: 1100px !important; +.ckyc-mega-panel { + border-top: 2.5px solid #be185d !important; } +/* Universal Mega Panel Grid styling inside Material Menu Overlays */ +.deposit-menu-grid { + display: grid !important; + gap: 4px 12px !important; + padding: 6px 10px !important; + box-sizing: border-box !important; + min-width: 0 !important; + width: 100% !important; + max-width: 100% !important; + overflow-x: hidden !important; +} + +/* Mega Panel Grids with exact column definitions */ +.deposit-menu-grid.cashier-grid, +.deposit-menu-grid.clerk-grid, +.deposit-menu-grid.clearing-grid, +.deposit-menu-grid.advances-grid, +.deposit-menu-grid.deposits-grid, +.deposit-menu-grid.hrm-grid, +.deposit-menu-grid.admin-grid, +.deposit-menu-grid.npa-grid, +.cashier-grid, +.clerk-grid, +.clearing-grid, +.advances-grid, +.deposits-grid, +.hrm-grid, +.admin-grid, +.npa-grid { + grid-template-columns: repeat(3, minmax(210px, 1fr)) !important; +} + +.deposit-menu-grid.reports-grid, +.reports-grid { + grid-template-columns: repeat(3, minmax(230px, 1fr)) !important; +} + +.deposit-menu-grid.branch-ops-grid, +.deposit-menu-grid.insurance-grid, +.deposit-menu-grid.digital-grid, +.deposit-menu-grid.ckyc-grid, +.branch-ops-grid, +.insurance-grid, +.digital-grid, +.ckyc-grid { + grid-template-columns: repeat(2, minmax(200px, 1fr)) !important; +} + +.deposit-menu-grid.share-grid, +.deposit-menu-grid.others-grid, +.share-grid, +.others-grid { + grid-template-columns: repeat(4, minmax(185px, 1fr)) !important; +} + +.deposit-menu-grid.letter-grid, +.deposit-menu-grid.reconciliation-grid, +.letter-grid, .reconciliation-grid { - grid-template-columns: repeat(5, minmax(190px, 1fr)) !important; - min-width: 1040px; + grid-template-columns: repeat(5, minmax(170px, 1fr)) !important; } -/* Admin Mega Menu — 3-column rectangular panel (Setup · Catalog · - Audit & Compliance). Reuses the deposit-mega-menu grid + group chrome - and retints the group-title accent to Admin purple. */ -.admin-mega-panel { - max-width: 760px !important; -} - -.admin-grid { - grid-template-columns: repeat(3, minmax(220px, 1fr)) !important; - min-width: 680px; +.deposit-menu-group { + display: flex !important; + flex-direction: column !important; + gap: 1px !important; + min-width: 0 !important; .deposit-group-title { - color: #7c3aed !important; - border-bottom-color: #7c3aed !important; + font-weight: 700 !important; + font-size: 9.5px !important; + text-transform: uppercase !important; + letter-spacing: 0.6px !important; + color: #1074b9 !important; + padding: 3px 6px !important; + margin-bottom: 2px !important; + border-bottom: 1.5px solid #1074b9 !important; + display: flex !important; + align-items: center !important; + line-height: 1.2 !important; + white-space: nowrap !important; + } +} + +/* Specific Accent Colors for Mega Panel Group Titles */ +.cashier-grid .deposit-group-title { + color: #0284c7 !important; + border-bottom-color: #0284c7 !important; +} + +.clerk-grid .deposit-group-title { + color: #2563eb !important; + border-bottom-color: #2563eb !important; +} + +.clearing-grid .deposit-group-title { + color: #0d9488 !important; + border-bottom-color: #0d9488 !important; +} + +.branch-ops-grid .deposit-group-title { + color: #4f46e5 !important; + border-bottom-color: #4f46e5 !important; +} + +.reports-grid .deposit-group-title { + color: #0369a1 !important; + border-bottom-color: #0369a1 !important; +} + +.insurance-grid .deposit-group-title { + color: #c2410c !important; + border-bottom-color: #c2410c !important; +} + +.digital-grid .deposit-group-title { + color: #7c3aed !important; + border-bottom-color: #7c3aed !important; +} + +.ckyc-grid .deposit-group-title { + color: #be185d !important; + border-bottom-color: #be185d !important; +} + +.admin-grid .deposit-group-title { + color: #7c3aed !important; + border-bottom-color: #7c3aed !important; +} + +.reconciliation-grid .deposit-group-title { + color: #0d9488 !important; + border-bottom-color: #0d9488 !important; +} + +/* Compact, RBI-Style Menu Item Overrides in CDK Overlays */ + +/* Compact, RBI-Style Menu Item Overrides for Navbar Mega Panels ONLY */ +.mat-mdc-menu-panel:has(.deposit-menu-grid) { + --mat-menu-item-label-text-size: 10.5px !important; + --mat-menu-item-label-text-line-height: 14px !important; + + border-radius: 8px !important; + box-shadow: + 0 10px 28px rgb(15 23 42 / 14%), + 0 2px 6px rgb(15 23 42 / 8%) !important; + border: 1px solid #d1d5db !important; + background: #fff !important; + max-height: 75vh !important; + overflow: hidden auto !important; + + .mat-mdc-menu-content { + padding: 4px 6px !important; + overflow-x: hidden !important; + } + + .deposit-menu-grid .mat-mdc-menu-item { + font-size: 10.5px !important; + font-weight: 500 !important; + height: 25px !important; + min-height: 25px !important; + line-height: 25px !important; + padding: 0 6px !important; + border-radius: 4px !important; + color: #334155 !important; + font-family: + 'IBM Plex Sans', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + sans-serif !important; + transition: + background-color 0.12s ease, + color 0.12s ease !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + min-width: 0 !important; + width: 100% !important; + box-sizing: border-box !important; + + .mat-mdc-menu-item-text, + .mdc-list-item__primary-text, + .mdc-list-item__content { + font-size: 10.5px !important; + font-weight: 500 !important; + line-height: 1.25 !important; + color: #334155 !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + display: inline-flex !important; + align-items: center !important; + width: 100% !important; + } + + .menu-item-icon, + fa-icon, + i { + font-size: 10.5px !important; + width: 13px !important; + min-width: 13px !important; + text-align: center !important; + margin-right: 5px !important; + opacity: 0.8 !important; + flex-shrink: 0 !important; + } + + &:hover { + background-color: #f0f7ff !important; + color: #0d47a1 !important; + + .mat-mdc-menu-item-text, + .mdc-list-item__primary-text { + color: #0d47a1 !important; + } + + .menu-item-icon, + fa-icon, + i { + opacity: 1 !important; + color: #0d47a1 !important; + } + } } } @@ -2012,3 +2278,43 @@ mifosx-client-datatable-step * { } } } + +// ===== CBS Compact Enterprise Density Polish ===== +// Reduces excessive whitespace and oversized elements across the banking UI +.mat-mdc-table { + th.mat-mdc-header-cell { + padding: 8px 12px !important; + font-size: 11px !important; + font-weight: 700 !important; + letter-spacing: 0.04em !important; + } + + td.mat-mdc-cell { + padding: 6px 12px !important; + font-size: 12px !important; + line-height: 1.35 !important; + } + + tr.mat-mdc-row, + tr.mat-mdc-header-row { + height: 36px !important; + min-height: 36px !important; + } +} + +// Compact Material Cards +.mat-mdc-card { + padding: 12px 16px !important; +} + +// Breadcrumb & Page Titles Compact Spacing +.breadcrumb-container { + margin-bottom: 8px !important; + padding: 4px 0 !important; +} + +.mifosx-page-title, +.page-title { + font-size: 17px !important; + margin-bottom: 8px !important; +} diff --git a/src/theme/mifosx-theme.scss b/src/theme/mifosx-theme.scss index 212aecd0e..0654f165f 100644 --- a/src/theme/mifosx-theme.scss +++ b/src/theme/mifosx-theme.scss @@ -53,7 +53,7 @@ $mifosx-typography: mat.m2-define-typography-config( $button: $mifosx-typography-level ); -$mifosx-density: 0; +$mifosx-density: -1; $mifosx-app-theme: mat.m2-define-light-theme( (