diff --git a/package.json b/package.json index aaf416c0..a22b2b8a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "ngx-admin", + "name": "identity-pulse", "version": "11.0.0", "license": "MIT", "repository": { diff --git a/src/app/@theme/components/footer/footer.component.ts b/src/app/@theme/components/footer/footer.component.ts index 8866debd..042de784 100644 --- a/src/app/@theme/components/footer/footer.component.ts +++ b/src/app/@theme/components/footer/footer.component.ts @@ -6,7 +6,7 @@ import { NbThemeService } from '@nebular/theme'; styleUrls: ['./footer.component.scss'], template: ` - Created with ♥ by Marketsoft 2025 + Powered by IdentityPulse © 2025
diff --git a/src/app/@theme/components/header/header.component.html b/src/app/@theme/components/header/header.component.html index 6fdc5d35..b5d2795d 100644 --- a/src/app/@theme/components/header/header.component.html +++ b/src/app/@theme/components/header/header.component.html @@ -3,7 +3,7 @@ - +
{{ theme.name }} diff --git a/src/app/@theme/components/header/header.component.ts b/src/app/@theme/components/header/header.component.ts index 14fa4e53..94c7bbe3 100644 --- a/src/app/@theme/components/header/header.component.ts +++ b/src/app/@theme/components/header/header.component.ts @@ -17,30 +17,30 @@ export class HeaderComponent implements OnInit, OnDestroy { userPictureOnly: boolean = false; user: any; -themes = [ - { - value: 'default', - name: 'Light', - }, - { - value: 'dark', - name: 'Dark', - }, - { - value: 'cosmic', - name: 'Cosmic', - }, - { - value: 'corporate', - name: 'Corporate', - }, - { - value: 'marketsoft', - name: 'Marketsoft', - }, -]; + themes = [ + { + value: 'default', + name: 'Light', + }, + { + value: 'dark', + name: 'Dark', + }, + { + value: 'cosmic', + name: 'Cosmic', + }, + { + value: 'corporate', + name: 'Corporate', + }, + { + value: 'marketsoft', + name: 'Marketsoft', + }, + ]; - currentTheme = 'default'; + currentTheme = 'marketsoft'; userMenu = [ { title: 'Profile' }, { title: 'Log out' } ]; @@ -95,4 +95,4 @@ themes = [ this.menuService.navigateHome(); return false; } -} +} \ No newline at end of file diff --git a/src/app/pages/countries/countries.component.html b/src/app/pages/countries/countries.component.html index a62e0de6..864d1bdd 100644 --- a/src/app/pages/countries/countries.component.html +++ b/src/app/pages/countries/countries.component.html @@ -2,56 +2,118 @@
-
Available Countries
+
Supported Countries & Regions
-

- Select from our available countries below to perform identity verification. Each country has different - data sources, update frequencies, and record counts. -

- -
-
- - -
- {{ country.icon }} - {{ country.name }} - - - - -
-
- -
-
- Records: {{ country.records }} -
-
- Updates: {{ country.updateFrequency }} -
-
- Status: {{ country.availability ? 'Available' : 'Coming Soon' }} -
-
-
- - - - - - -
-
-
+

IdentityPulse provides comprehensive identity verification coverage across Asia-Pacific and expanding into the Middle East.

+ + +
+
+
Active Countries
+
+
+ + +
+
+ {{ country.flag }} +
{{ country.name }}
+
+ +
+
+ +
+
+ Coverage + {{ country.coverage }}% +
+
+ Population + {{ country.population }} +
+
+ Databases + {{ country.databases }} +
+
+ Updates + {{ country.updateFrequency }} +
+
+ +
+
Available Features
+
+ {{ feature }} +
+
+ +
+
Compliance
+
+ {{ reg }} +
+
+
+ + + +
+
+
+ +
+
+
Coming Soon
+
+
+ + +
+
+ {{ country.flag }} +
{{ country.name }}
+
+ +
+
+ +
+
+ Population + {{ country.population }} +
+
+ +
+
Planned Features
+
+ {{ feature }} +
+
+ +
+
Target Compliance
+
+ {{ reg }} +
+
+
+ + + +
+
\ No newline at end of file diff --git a/src/app/pages/countries/countries.component.scss b/src/app/pages/countries/countries.component.scss index d9eb3645..16f3ed7e 100644 --- a/src/app/pages/countries/countries.component.scss +++ b/src/app/pages/countries/countries.component.scss @@ -1,12 +1,108 @@ -.country-icon { - font-size: 1.5rem; - margin-right: 10px; -} +@import '../../@theme/styles/themes'; -.country-info { - margin-bottom: 15px; -} +@include nb-install-component() { + .subtitle { + color: nb-theme(text-hint-color); + margin: 0; + } -.info-item { - margin-bottom: 5px; + .section-title { + margin: 2rem 0 1rem 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + .country-card { + height: 100%; + transition: all 0.3s ease; + + &:hover:not(.coming-soon) { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); + } + + &.coming-soon { + opacity: 0.8; + } + + .country-header { + display: flex; + justify-content: space-between; + align-items: center; + + .country-title { + display: flex; + align-items: center; + gap: 0.75rem; + + .flag { + font-size: 2rem; + } + + h6 { + margin: 0; + font-weight: 600; + } + } + } + + .country-stats { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-bottom: 1.5rem; + padding: 1rem; + background: nb-theme(background-basic-color-2); + border-radius: nb-theme(card-border-radius); + + .stat { + display: flex; + flex-direction: column; + + .label { + font-size: 0.75rem; + color: nb-theme(text-hint-color); + margin-bottom: 0.25rem; + } + + .value { + font-size: 1.25rem; + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + } + + .features, + .regulations { + margin-bottom: 1.5rem; + + h6 { + font-size: 0.875rem; + font-weight: 600; + margin: 0 0 0.75rem 0; + color: nb-theme(text-basic-color); + } + + .feature-tags, + .regulation-tags { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + + .tag { + display: inline-block; + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + background: nb-theme(background-basic-color-3); + border-radius: 1rem; + color: nb-theme(text-hint-color); + } + } + } + + nb-card-footer { + padding: 1rem; + } + } } \ No newline at end of file diff --git a/src/app/pages/countries/countries.component.ts b/src/app/pages/countries/countries.component.ts index f0298b8d..9bea97f4 100644 --- a/src/app/pages/countries/countries.component.ts +++ b/src/app/pages/countries/countries.component.ts @@ -1,65 +1,82 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; + +interface Country { + name: string; + flag: string; + status: 'active' | 'coming-soon'; + coverage: number; + population: string; + databases: number; + updateFrequency: string; + regulations: string[]; + features: string[]; +} @Component({ selector: 'ngx-countries', templateUrl: './countries.component.html', styleUrls: ['./countries.component.scss'] }) -export class CountriesComponent implements OnInit { - - countries = [ - { - name: 'Australia', - code: 'aus', - records: '15M+', +export class CountriesComponent { + + countries: Country[] = [ + { + name: 'Australia', + flag: '🇦🇺', + status: 'active', + coverage: 95, + population: '25.7M', + databases: 5, + updateFrequency: 'Daily', + regulations: ['Privacy Act 1988', 'AML/CTF Act', 'CDR'], + features: ['Full KYC', 'Document Verification', 'Biometric Matching', 'Watchlist Screening'] + }, + { + name: 'Indonesia', + flag: '🇮🇩', + status: 'active', + coverage: 82, + population: '273.5M', + databases: 4, updateFrequency: 'Weekly', - availability: true, - icon: '🇦🇺' + regulations: ['UU PDP', 'OJK Regulations'], + features: ['Identity Verification', 'Address Validation', 'Phone Verification', 'KTP Validation'] }, - { - name: 'Indonesia', - code: 'indo', - records: '120M+', - updateFrequency: 'Monthly', - availability: true, - icon: '🇮🇩' - }, - { - name: 'Japan', - code: 'japan', - records: '85M+', - updateFrequency: 'Bi-weekly', - availability: true, - icon: '🇯🇵' - }, - { - name: 'Malaysia', - code: 'malay', - records: '20M+', + { + name: 'Malaysia', + flag: '🇲🇾', + status: 'active', + coverage: 78, + population: '32.4M', + databases: 3, updateFrequency: 'Weekly', - availability: true, - icon: '🇲🇾' + regulations: ['PDPA 2010', 'AMLA'], + features: ['MyKad Verification', 'Address Check', 'Phone Validation', 'Business Registry'] }, - { - name: 'Singapore', - code: 'sg', - records: '5M+', - updateFrequency: 'Weekly', - availability: true, - icon: '🇸🇬' - }, - { - name: 'Thailand', - code: 'thai', - records: '45M+', - updateFrequency: 'Monthly', - availability: false, - icon: '🇹🇭' + { + name: 'Japan', + flag: '🇯🇵', + status: 'active', + coverage: 88, + population: '125.8M', + databases: 4, + updateFrequency: 'Daily', + regulations: ['APPI', 'FIEA'], + features: ['My Number Verification', 'Address Validation', 'Corporate Registry', 'AML Check'] }, + { + name: 'Saudi Arabia', + flag: '🇸🇦', + status: 'coming-soon', + coverage: 0, + population: '34.8M', + databases: 0, + updateFrequency: 'TBD', + regulations: ['PDPL', 'SAMA Regulations'], + features: ['National ID Verification', 'Iqama Validation', 'Business Registry', 'AML Screening'] + } ]; - constructor() { } - - ngOnInit(): void { - } + activeCountries = this.countries.filter(c => c.status === 'active'); + comingSoonCountries = this.countries.filter(c => c.status === 'coming-soon'); } \ No newline at end of file diff --git a/src/app/pages/countries/countries.module.ts b/src/app/pages/countries/countries.module.ts index 47db2592..0121908c 100644 --- a/src/app/pages/countries/countries.module.ts +++ b/src/app/pages/countries/countries.module.ts @@ -1,21 +1,27 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { NbCardModule, NbIconModule, NbListModule } from '@nebular/theme'; +import { + NbCardModule, + NbButtonModule, + NbIconModule, + NbBadgeModule +} from '@nebular/theme'; import { CountriesComponent } from './countries.component'; @NgModule({ imports: [ CommonModule, - NbCardModule, - NbIconModule, - NbListModule, RouterModule.forChild([ { path: '', component: CountriesComponent, }, ]), + NbCardModule, + NbButtonModule, + NbIconModule, + NbBadgeModule ], declarations: [ CountriesComponent, diff --git a/src/app/pages/dashboard/client-showcase/client-showcase.component.html b/src/app/pages/dashboard/client-showcase/client-showcase.component.html new file mode 100644 index 00000000..e0e520b7 --- /dev/null +++ b/src/app/pages/dashboard/client-showcase/client-showcase.component.html @@ -0,0 +1,42 @@ + + +
Trusted by Industry Leaders
+
+ +
+
+ +
+
{{ client.name }}
+ {{ client.industry }} +

{{ client.description }}

+
+ + {{ client.verifications }} verifications +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/client-showcase/client-showcase.component.scss b/src/app/pages/dashboard/client-showcase/client-showcase.component.scss new file mode 100644 index 00000000..bde60d89 --- /dev/null +++ b/src/app/pages/dashboard/client-showcase/client-showcase.component.scss @@ -0,0 +1,137 @@ +@import '../../../@theme/styles/themes'; +@import 'bootstrap/scss/mixins/breakpoints'; +@import '@nebular/theme/styles/global/breakpoints'; + +@include nb-install-component() { + nb-card { + height: 100%; + } + + nb-card-header { + h6 { + margin: 0; + font-weight: 600; + } + } + + .clients-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-bottom: 2rem; + + @include media-breakpoint-down(sm) { + grid-template-columns: 1fr; + } + } + + .client-card { + display: flex; + align-items: center; + padding: 1rem; + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + transition: all 0.3s ease; + cursor: pointer; + + &:hover { + border-color: nb-theme(color-primary-default); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } + } + + .client-logo { + flex-shrink: 0; + width: 60px; + height: 60px; + margin-right: 1rem; + + .logo-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: nb-theme(background-basic-color-3); + border-radius: nb-theme(card-border-radius); + font-size: 0.625rem; + font-weight: 600; + text-align: center; + padding: 0.5rem; + color: nb-theme(text-hint-color); + } + } + + .client-info { + flex: 1; + min-width: 0; + + .client-name { + margin: 0 0 0.25rem 0; + font-size: 0.875rem; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + .client-industry { + display: inline-block; + font-size: 0.75rem; + color: nb-theme(text-hint-color); + margin-bottom: 0.5rem; + } + + .client-description { + font-size: 0.75rem; + color: nb-theme(text-hint-color); + margin: 0 0 0.5rem 0; + display: none; + + @include media-breakpoint-up(lg) { + display: block; + } + } + + .verification-count { + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + color: nb-theme(color-success-default); + + nb-icon { + font-size: 1rem; + } + } + } + + .showcase-footer { + border-top: 1px solid nb-theme(border-basic-color-3); + padding-top: 1.5rem; + margin-top: 1rem; + } + + .trust-metrics { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + text-align: center; + + .metric { + display: flex; + flex-direction: column; + + .metric-value { + font-size: 1.5rem; + font-weight: 700; + color: nb-theme(color-primary-default); + margin-bottom: 0.25rem; + } + + .metric-label { + font-size: 0.75rem; + color: nb-theme(text-hint-color); + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/client-showcase/client-showcase.component.ts b/src/app/pages/dashboard/client-showcase/client-showcase.component.ts new file mode 100644 index 00000000..be841f5f --- /dev/null +++ b/src/app/pages/dashboard/client-showcase/client-showcase.component.ts @@ -0,0 +1,79 @@ +import { Component, OnDestroy } from '@angular/core'; +import { NbThemeService } from '@nebular/theme'; +import { takeWhile } from 'rxjs/operators'; + +interface Client { + name: string; + logo: string; + industry: string; + description: string; + verifications: string; +} + +@Component({ + selector: 'ngx-client-showcase', + styleUrls: ['./client-showcase.component.scss'], + templateUrl: './client-showcase.component.html', +}) +export class ClientShowcaseComponent implements OnDestroy { + + private alive = true; + currentTheme: string; + + clients: Client[] = [ + { + name: 'LexisNexis', + logo: 'assets/images/clients/lexisnexis.png', + industry: 'Risk Solutions', + description: 'Global leader in legal and business information', + verifications: '2.3M+' + }, + { + name: 'Data Zoo', + logo: 'assets/images/clients/datazoo.png', + industry: 'Identity Verification', + description: 'Identity verification and fraud prevention', + verifications: '1.8M+' + }, + { + name: 'Commonwealth Bank', + logo: 'assets/images/clients/cba.png', + industry: 'Banking', + description: 'Australia\'s leading financial institution', + verifications: '3.1M+' + }, + { + name: 'Westpac', + logo: 'assets/images/clients/westpac.png', + industry: 'Banking', + description: 'Major Australian bank and financial services', + verifications: '2.7M+' + }, + { + name: 'Telstra', + logo: 'assets/images/clients/telstra.png', + industry: 'Telecommunications', + description: 'Australia\'s largest telecommunications company', + verifications: '1.5M+' + }, + { + name: 'ANZ Bank', + logo: 'assets/images/clients/anz.png', + industry: 'Banking', + description: 'Leading bank across Australia and New Zealand', + verifications: '2.4M+' + } + ]; + + constructor(private themeService: NbThemeService) { + this.themeService.getJsTheme() + .pipe(takeWhile(() => this.alive)) + .subscribe(theme => { + this.currentTheme = theme.name; + }); + } + + ngOnDestroy() { + this.alive = false; + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/compliance-summary/compliance-summary.component.html b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.html new file mode 100644 index 00000000..ab207ddf --- /dev/null +++ b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.html @@ -0,0 +1,40 @@ + + +
Compliance & Data Residency
+
+ +
+
+
+ {{ item.region }} + + +
+ +
+ + {{ reg }} + +
+ +
+ + {{ item.hostingModel }} +
+
+
+ +
+
Certifications
+
+
+ + {{ cert.name }} +
+
+
+
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/compliance-summary/compliance-summary.component.scss b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.scss new file mode 100644 index 00000000..bd3521a7 --- /dev/null +++ b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.scss @@ -0,0 +1,92 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card-header h6 { + margin: 0; + font-weight: 600; + } + + .compliance-list { + display: flex; + flex-direction: column; + gap: 1rem; + margin-bottom: 1.5rem; + } + + .compliance-item { + padding: 1rem; + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + + .item-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; + + .region { + font-weight: 600; + color: nb-theme(text-basic-color); + } + + nb-icon { + font-size: 1.25rem; + } + } + + .regulations { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.75rem; + + .regulation { + font-size: 0.75rem; + padding: 0.25rem 0.5rem; + background: nb-theme(background-basic-color-3); + border-radius: nb-theme(border-radius); + color: nb-theme(text-hint-color); + } + } + + .hosting-model { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: nb-theme(text-hint-color); + + nb-icon { + font-size: 1rem; + } + } + } + + .certifications { + h6 { + margin: 0 0 0.75rem 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + .cert-badges { + display: flex; + flex-direction: column; + gap: 0.5rem; + + .badge { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: nb-theme(background-basic-color-2); + border-radius: nb-theme(card-border-radius); + font-size: 0.875rem; + + nb-icon { + font-size: 1.25rem; + } + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/compliance-summary/compliance-summary.component.ts b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.ts new file mode 100644 index 00000000..f9f33742 --- /dev/null +++ b/src/app/pages/dashboard/compliance-summary/compliance-summary.component.ts @@ -0,0 +1,75 @@ +import { Component } from '@angular/core'; + +interface ComplianceItem { + region: string; + regulations: string[]; + status: 'compliant' | 'in-progress' | 'planned'; + hostingModel: string; +} + +@Component({ + selector: 'ngx-compliance-summary', + styleUrls: ['./compliance-summary.component.scss'], + templateUrl: './compliance-summary.component.html', +}) +export class ComplianceSummaryComponent { + + complianceItems: ComplianceItem[] = [ + { + region: 'Australia', + regulations: ['Privacy Act 1988', 'AML/CTF Act', 'CDR'], + status: 'compliant', + hostingModel: 'Local data residency' + }, + { + region: 'Indonesia', + regulations: ['UU PDP', 'OJK Regulations'], + status: 'compliant', + hostingModel: 'Local data center' + }, + { + region: 'Malaysia', + regulations: ['PDPA 2010', 'AMLA'], + status: 'compliant', + hostingModel: 'Regional hosting' + }, + { + region: 'Japan', + regulations: ['APPI', 'FIEA'], + status: 'compliant', + hostingModel: 'Local data residency' + } + ]; + + certifications = [ + { name: 'ISO 27001', icon: 'shield-outline' }, + { name: 'SOC 2 Type II', icon: 'lock-outline' }, + { name: 'GDPR Ready', icon: 'checkmark-square-outline' } + ]; + + getStatusColor(status: string): string { + switch (status) { + case 'compliant': + return 'success'; + case 'in-progress': + return 'warning'; + case 'planned': + return 'info'; + default: + return 'basic'; + } + } + + getStatusIcon(status: string): string { + switch (status) { + case 'compliant': + return 'checkmark-circle-2-outline'; + case 'in-progress': + return 'sync-outline'; + case 'planned': + return 'calendar-outline'; + default: + return 'minus-circle-outline'; + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/country-coverage/country-coverage.component.html b/src/app/pages/dashboard/country-coverage/country-coverage.component.html new file mode 100644 index 00000000..21659564 --- /dev/null +++ b/src/app/pages/dashboard/country-coverage/country-coverage.component.html @@ -0,0 +1,44 @@ + + + Country Coverage & Availability + + All Regions + APAC + MENA + Europe + + + + +
+
+
+
+
{{ country.name }}
+ + +
+
+ {{ country.records }} records + {{ country.updateFrequency }} updates +
+
+ +
+
+ Coverage + {{ country.coverage }}% +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/country-coverage/country-coverage.component.scss b/src/app/pages/dashboard/country-coverage/country-coverage.component.scss new file mode 100644 index 00000000..74cf317b --- /dev/null +++ b/src/app/pages/dashboard/country-coverage/country-coverage.component.scss @@ -0,0 +1,117 @@ +@import '../../../@theme/styles/themes'; +@import 'bootstrap/scss/mixins/breakpoints'; +@import '@nebular/theme/styles/global/breakpoints'; + +@include nb-install-component() { + nb-card-header { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: nb-theme(card-header-with-select-padding-top); + padding-bottom: nb-theme(card-header-with-select-padding-bottom); + } + + .region-select { + margin-left: auto; + min-width: 8rem; + } + + .country-list { + max-height: 400px; + overflow-y: auto; + padding-right: 0.5rem; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: nb-theme(background-basic-color-2); + } + + &::-webkit-scrollbar-thumb { + background: nb-theme(background-basic-color-4); + border-radius: 3px; + } + } + + .country-item { + padding: 1rem; + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + margin-bottom: 1rem; + transition: all 0.3s ease; + + &:hover { + border-color: nb-theme(color-primary-default); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } + + &:last-child { + margin-bottom: 0; + } + } + + .country-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.75rem; + } + + .country-info { + display: flex; + align-items: center; + gap: 0.5rem; + + .country-name { + margin: 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + nb-icon { + font-size: 1.25rem; + } + } + + .country-stats { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.25rem; + + span { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + } + } + + .coverage-section { + .coverage-header { + display: flex; + justify-content: space-between; + margin-bottom: 0.5rem; + + .coverage-label { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + } + + .coverage-value { + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + + nb-progress-bar { + height: 0.5rem; + } + } + + @include media-breakpoint-down(sm) { + .country-stats { + display: none; + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/country-coverage/country-coverage.component.ts b/src/app/pages/dashboard/country-coverage/country-coverage.component.ts new file mode 100644 index 00000000..984c10f1 --- /dev/null +++ b/src/app/pages/dashboard/country-coverage/country-coverage.component.ts @@ -0,0 +1,101 @@ +import { Component, OnDestroy } from '@angular/core'; +import { NbThemeService } from '@nebular/theme'; +import { takeWhile } from 'rxjs/operators'; + +interface CountryData { + name: string; + coverage: number; + records: string; + updateFrequency: string; + status: 'active' | 'coming-soon' | 'limited'; +} + +@Component({ + selector: 'ngx-country-coverage', + styleUrls: ['./country-coverage.component.scss'], + templateUrl: './country-coverage.component.html', +}) +export class CountryCoverageComponent implements OnDestroy { + + private alive = true; + + countries: CountryData[] = [ + { + name: 'Australia', + coverage: 95, + records: '25.6M', + updateFrequency: 'Daily', + status: 'active' + }, + { + name: 'Indonesia', + coverage: 82, + records: '180.2M', + updateFrequency: 'Weekly', + status: 'active' + }, + { + name: 'Malaysia', + coverage: 78, + records: '28.4M', + updateFrequency: 'Weekly', + status: 'active' + }, + { + name: 'Japan', + coverage: 88, + records: '126.8M', + updateFrequency: 'Daily', + status: 'active' + }, + ]; + + selectedRegion = 'all'; + regions = ['all', 'apac', 'mena', 'europe']; + + currentTheme: string; + + constructor(private themeService: NbThemeService) { + this.themeService.getJsTheme() + .pipe(takeWhile(() => this.alive)) + .subscribe(theme => { + this.currentTheme = theme.name; + }); + } + + ngOnDestroy() { + this.alive = false; + } + + getStatusIcon(status: string): string { + switch (status) { + case 'active': + return 'checkmark-circle-2-outline'; + case 'coming-soon': + return 'clock-outline'; + case 'limited': + return 'alert-triangle-outline'; + default: + return 'minus-circle-outline'; + } + } + + getStatusColor(status: string): string { + switch (status) { + case 'active': + return 'success'; + case 'coming-soon': + return 'warning'; + case 'limited': + return 'danger'; + default: + return 'basic'; + } + } + + getCoverageStatus(coverage: number): string { + if (coverage >= 80) return 'success'; + if (coverage >= 60) return 'warning'; + return 'danger'; + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.component.html b/src/app/pages/dashboard/dashboard.component.html index 6b99758a..442f16f9 100644 --- a/src/app/pages/dashboard/dashboard.component.html +++ b/src/app/pages/dashboard/dashboard.component.html @@ -1,42 +1,45 @@
- - - + +
-
- +
+
-
- +
+ +
+
+ +
+
+ +
+ +
+
-
- +
+
-
- +
+
-
- - - +
+
- -
- - -
- -
- -
-
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.component.scss b/src/app/pages/dashboard/dashboard.component.scss index 223bcbe6..4661181b 100644 --- a/src/app/pages/dashboard/dashboard.component.scss +++ b/src/app/pages/dashboard/dashboard.component.scss @@ -13,4 +13,4 @@ display: none; } } -} +} \ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.component.ts b/src/app/pages/dashboard/dashboard.component.ts index c359216a..00990f3f 100644 --- a/src/app/pages/dashboard/dashboard.component.ts +++ b/src/app/pages/dashboard/dashboard.component.ts @@ -1,10 +1,11 @@ -import {Component, OnDestroy} from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { NbThemeService } from '@nebular/theme'; -import { takeWhile } from 'rxjs/operators' ; -import { SolarData } from '../../@core/data/solar'; +import { takeWhile } from 'rxjs/operators'; interface CardSettings { title: string; + value: string; + unitOfMeasurement: string; iconClass: string; type: string; } @@ -18,35 +19,46 @@ export class DashboardComponent implements OnDestroy { private alive = true; - solarValue: number; - lightCard: CardSettings = { - title: 'Light', - iconClass: 'nb-lightbulb', + // Key metrics for IdentityPulse + verificationCard: CardSettings = { + title: 'Total Verifications', + value: '1,247', + unitOfMeasurement: 'today', + iconClass: 'nb-checkmark-circle', type: 'primary', }; - rollerShadesCard: CardSettings = { - title: 'Roller Shades', - iconClass: 'nb-roller-shades', + + matchRateCard: CardSettings = { + title: 'Average Match Rate', + value: '87.3', + unitOfMeasurement: '%', + iconClass: 'nb-bar-chart', type: 'success', }; - wirelessAudioCard: CardSettings = { - title: 'Wireless Audio', - iconClass: 'nb-audio', + + countriesCard: CardSettings = { + title: 'Active Countries', + value: '4', + unitOfMeasurement: 'regions', + iconClass: 'nb-location', type: 'info', }; - coffeeMakerCard: CardSettings = { - title: 'Coffee Maker', - iconClass: 'nb-coffee-maker', + + apiResponseCard: CardSettings = { + title: 'API Response Time', + value: '245', + unitOfMeasurement: 'ms', + iconClass: 'nb-gear', type: 'warning', }; - statusCards: string; + statusCards: CardSettings[]; commonStatusCardsSet: CardSettings[] = [ - this.lightCard, - this.rollerShadesCard, - this.wirelessAudioCard, - this.coffeeMakerCard, + this.verificationCard, + this.matchRateCard, + this.countriesCard, + this.apiResponseCard, ]; statusCardsByThemes: { @@ -54,46 +66,24 @@ export class DashboardComponent implements OnDestroy { cosmic: CardSettings[]; corporate: CardSettings[]; dark: CardSettings[]; + marketsoft: CardSettings[]; } = { default: this.commonStatusCardsSet, cosmic: this.commonStatusCardsSet, - corporate: [ - { - ...this.lightCard, - type: 'warning', - }, - { - ...this.rollerShadesCard, - type: 'primary', - }, - { - ...this.wirelessAudioCard, - type: 'danger', - }, - { - ...this.coffeeMakerCard, - type: 'info', - }, - ], + corporate: this.commonStatusCardsSet, dark: this.commonStatusCardsSet, + marketsoft: this.commonStatusCardsSet, }; - constructor(private themeService: NbThemeService, - private solarService: SolarData) { + constructor(private themeService: NbThemeService) { this.themeService.getJsTheme() .pipe(takeWhile(() => this.alive)) .subscribe(theme => { this.statusCards = this.statusCardsByThemes[theme.name]; - }); - - this.solarService.getSolarData() - .pipe(takeWhile(() => this.alive)) - .subscribe((data) => { - this.solarValue = data; }); } ngOnDestroy() { this.alive = false; } -} +} \ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.module.ts b/src/app/pages/dashboard/dashboard.module.ts index b938dd67..c3a4ff7e 100644 --- a/src/app/pages/dashboard/dashboard.module.ts +++ b/src/app/pages/dashboard/dashboard.module.ts @@ -9,6 +9,7 @@ import { NbSelectModule, NbListModule, NbIconModule, + NbProgressBarModule, } from '@nebular/theme'; import { NgxEchartsModule } from 'ngx-echarts'; @@ -31,6 +32,19 @@ import { TrafficComponent } from './traffic/traffic.component'; import { TrafficChartComponent } from './traffic/traffic-chart.component'; import { FormsModule } from '@angular/forms'; +// IdentityPulse components +import { MetricCardComponent } from './metric-card/metric-card.component'; +import { CountryCoverageComponent } from './country-coverage/country-coverage.component'; +import { ClientShowcaseComponent } from './client-showcase/client-showcase.component'; +import { DataCentersMapComponent } from './data-centers-map/data-centers-map.component'; +import { RecentVerificationsComponent } from './recent-verifications/recent-verifications.component'; +import { PricingRegionsComponent } from './pricing-regions/pricing-regions.component'; +import { ComplianceSummaryComponent } from './compliance-summary/compliance-summary.component'; +import { DifferentiatorsComponent } from './differentiators/differentiators.component'; + +// Note: ECharts is already available globally through ngx-echarts +// The world map is registered by ngx-admin's theme module + @NgModule({ imports: [ FormsModule, @@ -45,6 +59,7 @@ import { FormsModule } from '@angular/forms'; NbListModule, NbIconModule, NbButtonModule, + NbProgressBarModule, NgxEchartsModule, ], declarations: [ @@ -64,6 +79,15 @@ import { FormsModule } from '@angular/forms'; SolarComponent, TrafficComponent, TrafficChartComponent, + // IdentityPulse components + MetricCardComponent, + CountryCoverageComponent, + ClientShowcaseComponent, + DataCentersMapComponent, + RecentVerificationsComponent, + PricingRegionsComponent, + ComplianceSummaryComponent, + DifferentiatorsComponent, ], }) -export class DashboardModule { } +export class DashboardModule { } \ No newline at end of file diff --git a/src/app/pages/dashboard/data-centers-map/data-centers-map.component.html b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.html new file mode 100644 index 00000000..0ac9d8b1 --- /dev/null +++ b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.html @@ -0,0 +1,38 @@ + + +
+
Global Identity Verification Coverage
+
+ + + Online + + + + Maintenance + + + + Offline + +
+
+
+ +
+
+
+ + {{ getCountryCount() }} Countries +
+
+ + {{ getTotalDatabases() }} Databases +
+
+ + {{ getOnlineCount() }} Online +
+
+
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/data-centers-map/data-centers-map.component.scss b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.scss new file mode 100644 index 00000000..58048907 --- /dev/null +++ b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.scss @@ -0,0 +1,88 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card { + height: 450px; + } + + .header-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + h6 { + margin: 0; + font-weight: 600; + } + } + + .legend { + display: flex; + gap: 1rem; + + .legend-item { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: nb-theme(text-hint-color); + + .dot { + width: 10px; + height: 10px; + border-radius: 50%; + + &.online { + background-color: #00d68f; + } + + &.maintenance { + background-color: #ffaa00; + } + + &.offline { + background-color: #ff3d71; + } + } + } + } + + nb-card-body { + position: relative; + padding: 0; + } + + .map-container { + width: 100%; + height: 350px; + z-index: 1; + } + + .map-stats { + position: absolute; + bottom: 1rem; + left: 1rem; + background: nb-theme(background-basic-color-1); + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + padding: 0.75rem 1rem; + display: flex; + gap: 1.5rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + z-index: 1000; // Ensure stats appear above the map + + .stat { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: nb-theme(text-basic-color); + + nb-icon { + font-size: 1.25rem; + color: nb-theme(color-primary-default); + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/data-centers-map/data-centers-map.component.ts b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.ts new file mode 100644 index 00000000..e9881bd9 --- /dev/null +++ b/src/app/pages/dashboard/data-centers-map/data-centers-map.component.ts @@ -0,0 +1,215 @@ +import { Component, OnDestroy, AfterViewInit } from '@angular/core'; +import { NbThemeService } from '@nebular/theme'; +import { Router } from '@angular/router'; +import { takeWhile } from 'rxjs/operators'; + +declare const L: any; + +interface DataCenter { + name: string; + country: string; + city: string; + coordinates: [number, number]; + status: 'online' | 'maintenance' | 'offline'; + databases: number; + responseTime: number; +} + +@Component({ + selector: 'ngx-data-centers-map', + styleUrls: ['./data-centers-map.component.scss'], + templateUrl: './data-centers-map.component.html', +}) +export class DataCentersMapComponent implements AfterViewInit, OnDestroy { + + private alive = true; + private map: any; + + dataCenters: DataCenter[] = [ + { + name: 'Sydney DC1', + country: 'Australia', + city: 'Sydney', + coordinates: [-33.8688, 151.2093], + status: 'online', + databases: 3, + responseTime: 45 + }, + { + name: 'Melbourne DC2', + country: 'Australia', + city: 'Melbourne', + coordinates: [-37.8136, 144.9631], + status: 'online', + databases: 2, + responseTime: 52 + }, + { + name: 'Jakarta DC1', + country: 'Indonesia', + city: 'Jakarta', + coordinates: [-6.2088, 106.8456], + status: 'online', + databases: 4, + responseTime: 78 + }, + { + name: 'Kuala Lumpur DC1', + country: 'Malaysia', + city: 'Kuala Lumpur', + coordinates: [3.1390, 101.6869], + status: 'online', + databases: 2, + responseTime: 65 + }, + { + name: 'Tokyo DC1', + country: 'Japan', + city: 'Tokyo', + coordinates: [35.6762, 139.6503], + status: 'online', + databases: 3, + responseTime: 89 + } + ]; + + constructor( + private theme: NbThemeService, + private router: Router + ) {} + + ngAfterViewInit() { + // Wait for the view to be fully initialized + setTimeout(() => { + this.initMap(); + }, 100); + } + + initMap() { + // Initialize the map centered on Asia-Pacific region + this.map = L.map('map', { + center: [0, 120], + zoom: 4, + minZoom: 2, + maxZoom: 18, + maxBounds: [[-90, -180], [90, 180]], + maxBoundsViscosity: 1.0, + zoomControl: true, + attributionControl: false + }); + + // Add tile layer with a professional style + L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { + maxZoom: 19, + subdomains: 'abcd', + noWrap: true // Prevent the world from repeating + }).addTo(this.map); + + // Add markers for each data center + this.dataCenters.forEach(dc => { + const icon = this.createCustomIcon(dc.status); + + const marker = L.marker(dc.coordinates, { icon }) + .addTo(this.map) + .bindPopup(this.createPopupContent(dc)); + + // Add click event to navigate to identity lookup + marker.on('click', () => { + setTimeout(() => { + this.router.navigate(['/pages/identity/manual-lookup'], { + queryParams: { country: dc.country.toLowerCase() } + }); + }, 1000); + }); + }); + + // Fit map to show all markers + const group = new L.featureGroup(this.dataCenters.map(dc => L.marker(dc.coordinates))); + this.map.fitBounds(group.getBounds().pad(0.1)); + } + + createCustomIcon(status: string) { + const colors = { + online: '#00d68f', + maintenance: '#ffaa00', + offline: '#ff3d71' + }; + + const html = ` +
+ `; + + return L.divIcon({ + html: html, + className: 'custom-marker', + iconSize: [24, 24], + iconAnchor: [12, 12] + }); + } + + createPopupContent(dc: DataCenter): string { + const statusColors = { + online: '#00d68f', + maintenance: '#ffaa00', + offline: '#ff3d71' + }; + + return ` +
+
${dc.name}
+
+
City: ${dc.city}, ${dc.country}
+
Status: ${dc.status}
+
Databases: ${dc.databases}
+
Response Time: ${dc.responseTime}ms
+
+
+ + Click to query this region → + +
+
+ `; + } + + getTotalDatabases(): number { + return this.dataCenters.reduce((sum, dc) => sum + dc.databases, 0); + } + + getOnlineCount(): number { + return this.dataCenters.filter(dc => dc.status === 'online').length; + } + + getCountryCount(): number { + // Get unique countries + const uniqueCountries = new Set(this.dataCenters.map(dc => dc.country)); + return uniqueCountries.size; + } + + getStatusColor(status: string): string { + switch (status) { + case 'online': + return '#00d68f'; + case 'maintenance': + return '#ffaa00'; + case 'offline': + return '#ff3d71'; + default: + return '#8f9bb3'; + } + } + + ngOnDestroy() { + this.alive = false; + if (this.map) { + this.map.remove(); + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/differentiators/differentiators.component.html b/src/app/pages/dashboard/differentiators/differentiators.component.html new file mode 100644 index 00000000..67d0d1aa --- /dev/null +++ b/src/app/pages/dashboard/differentiators/differentiators.component.html @@ -0,0 +1,27 @@ + + +
Why IdentityPulse is Different
+
+ +
+
+
+ +
+ +
+
{{ item.title }}
+

{{ item.description }}

+ {{ item.metric }} +
+
+
+ +
+

Experience the difference with a live demo

+ +
+
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/differentiators/differentiators.component.scss b/src/app/pages/dashboard/differentiators/differentiators.component.scss new file mode 100644 index 00000000..752197a1 --- /dev/null +++ b/src/app/pages/dashboard/differentiators/differentiators.component.scss @@ -0,0 +1,90 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card-header h6 { + margin: 0; + font-weight: 600; + } + + .differentiators-grid { + display: flex; + flex-direction: column; + gap: 1rem; + margin-bottom: 1.5rem; + } + + .differentiator { + display: flex; + gap: 1rem; + padding: 1rem; + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + transition: all 0.3s ease; + + &:hover { + border-color: nb-theme(color-primary-default); + transform: translateX(4px); + + .icon-wrapper { + background: nb-theme(color-primary-default); + color: white; + } + } + + .icon-wrapper { + flex-shrink: 0; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + background: nb-theme(background-basic-color-3); + border-radius: nb-theme(card-border-radius); + transition: all 0.3s ease; + + nb-icon { + font-size: 1.5rem; + } + } + + .content { + flex: 1; + + .title { + margin: 0 0 0.25rem 0; + font-size: 0.875rem; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + .description { + margin: 0 0 0.5rem 0; + font-size: 0.75rem; + color: nb-theme(text-hint-color); + line-height: 1.4; + } + + .metric { + display: inline-block; + font-size: 0.75rem; + font-weight: 600; + color: nb-theme(color-primary-default); + padding: 0.125rem 0.5rem; + background: nb-theme(color-primary-transparent-100); + border-radius: nb-theme(border-radius); + } + } + } + + .cta-section { + text-align: center; + padding-top: 1rem; + border-top: 1px solid nb-theme(border-basic-color-3); + + .cta-text { + margin: 0 0 1rem 0; + font-size: 0.875rem; + color: nb-theme(text-hint-color); + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/differentiators/differentiators.component.ts b/src/app/pages/dashboard/differentiators/differentiators.component.ts new file mode 100644 index 00000000..e7550328 --- /dev/null +++ b/src/app/pages/dashboard/differentiators/differentiators.component.ts @@ -0,0 +1,55 @@ +import { Component } from '@angular/core'; + +interface Differentiator { + title: string; + description: string; + icon: string; + metric?: string; +} + +@Component({ + selector: 'ngx-differentiators', + styleUrls: ['./differentiators.component.scss'], + templateUrl: './differentiators.component.html', +}) +export class DifferentiatorsComponent { + + differentiators: Differentiator[] = [ + { + title: 'Real-time Updates', + description: 'Direct integration with government and trusted sources for instant data updates', + icon: 'flash-outline', + metric: '< 5min latency' + }, + { + title: 'Multi-Source Validation', + description: 'Cross-reference data from multiple authoritative sources for accuracy', + icon: 'layers-outline', + metric: '15+ sources' + }, + { + title: 'AI-Powered Matching', + description: 'Advanced fuzzy matching algorithms handle name variations and typos', + icon: 'bulb-outline', + metric: '99.2% accuracy' + }, + { + title: 'Regional Expertise', + description: 'Deep understanding of local naming conventions and data formats', + icon: 'globe-2-outline', + metric: '4 regions' + }, + { + title: 'Elastic Infrastructure', + description: 'Built on Elasticsearch for lightning-fast queries at any scale', + icon: 'activity-outline', + metric: '< 250ms response' + }, + { + title: 'Privacy by Design', + description: 'Zero data retention policy and end-to-end encryption', + icon: 'shield-outline', + metric: 'ISO 27001' + } + ]; +} \ No newline at end of file diff --git a/src/app/pages/dashboard/metric-card/metric-card.component.scss b/src/app/pages/dashboard/metric-card/metric-card.component.scss new file mode 100644 index 00000000..af3be796 --- /dev/null +++ b/src/app/pages/dashboard/metric-card/metric-card.component.scss @@ -0,0 +1,95 @@ +@import '../../../@theme/styles/themes'; +@import 'bootstrap/scss/mixins/breakpoints'; +@import '@nebular/theme/styles/global/breakpoints'; + +@include nb-install-component() { + nb-card { + flex-direction: row; + align-items: center; + height: 6rem; + overflow: visible; + + .icon-container { + height: 100%; + padding: 0.625rem; + } + + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 5.75rem; + height: 4.75rem; + font-size: 3.75rem; + border-radius: nb-theme(card-border-radius); + transition: width 0.4s ease; + transform: translate3d(0, 0, 0); + -webkit-transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + color: nb-theme(text-control-color); + + @each $status in nb-get-statuses() { + &.status-#{$status} { + $left-color: nb-theme(button-hero-#{$status}-left-background-color); + $right-color: nb-theme(button-hero-#{$status}-right-background-color); + background-image: linear-gradient(to right, $left-color, $right-color); + + &:hover { + $left-hover-color: nb-theme(button-hero-#{$status}-hover-left-background-color); + $right-hover-color: nb-theme(button-hero-#{$status}-hover-right-background-color); + background-image: linear-gradient(to right, $left-hover-color, $right-hover-color); + } + } + } + } + + .details { + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + @include nb-ltr(padding, 0 0.5rem 0 0.75rem); + @include nb-rtl(padding, 0 0.75rem 0 0.5rem); + border-left: 1px solid transparent; + } + + .title { + margin: 0; + font-weight: 600; + color: nb-theme(text-hint-color); + font-size: 0.875rem; + } + + .status-value { + display: flex; + align-items: baseline; + + .value { + margin: 0; + font-weight: 700; + color: nb-theme(text-basic-color); + font-size: 2rem; + } + + .unit { + margin-left: 0.5rem; + color: nb-theme(text-hint-color); + font-size: 0.875rem; + } + } + } + + @include media-breakpoint-down(sm) { + nb-card { + .icon { + width: 4rem; + height: 4rem; + font-size: 2.5rem; + } + + .value { + font-size: 1.5rem; + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/metric-card/metric-card.component.ts b/src/app/pages/dashboard/metric-card/metric-card.component.ts new file mode 100644 index 00000000..19d12e01 --- /dev/null +++ b/src/app/pages/dashboard/metric-card/metric-card.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ngx-metric-card', + styleUrls: ['./metric-card.component.scss'], + template: ` + +
+
+ +
+
+ +
+
{{ title }}
+
+ {{ value }} + {{ unitOfMeasurement }} +
+
+
+ `, +}) +export class MetricCardComponent { + @Input() title: string; + @Input() type: string; + @Input() value: string; + @Input() unitOfMeasurement: string; + @Input() iconClass: string; +} \ No newline at end of file diff --git a/src/app/pages/dashboard/pricing-regions/pricing-regions.component.html b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.html new file mode 100644 index 00000000..bca9b332 --- /dev/null +++ b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.html @@ -0,0 +1,47 @@ + + +
Regional Pricing
+
+ +
+
+
+ {{ tier.flag }} +
{{ tier.region }}
+
+ +
+
+ {{ tier.currency }} + ${{ tier.basePrice }} + /month +
+ +
+ Per verification: + ${{ tier.perVerification }} +
+ +
+ + {{ tier.volumeDiscount }} +
+
+
+
+ +
+
All plans include:
+
    +
  • + + {{ feature }} +
  • +
+
+ + +
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/pricing-regions/pricing-regions.component.scss b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.scss new file mode 100644 index 00000000..df57bf5a --- /dev/null +++ b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.scss @@ -0,0 +1,115 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card-header h6 { + margin: 0; + font-weight: 600; + } + + .pricing-tiers { + display: flex; + flex-direction: column; + gap: 1rem; + margin-bottom: 1.5rem; + } + + .tier { + padding: 1rem; + border: 1px solid nb-theme(border-basic-color-3); + border-radius: nb-theme(card-border-radius); + transition: all 0.3s ease; + + &:hover { + border-color: nb-theme(color-primary-default); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } + + .tier-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + + .region-flag { + font-size: 1.5rem; + } + + .region-name { + margin: 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + + .pricing-details { + .base-price { + display: flex; + align-items: baseline; + margin-bottom: 0.5rem; + + .currency { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + margin-right: 0.25rem; + } + + .price { + font-size: 1.5rem; + font-weight: 700; + color: nb-theme(color-primary-default); + } + + .period { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + margin-left: 0.25rem; + } + } + + .per-verification, + .volume-discount { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: nb-theme(text-hint-color); + margin-bottom: 0.25rem; + + nb-icon { + font-size: 1rem; + color: nb-theme(color-success-default); + } + } + } + } + + .features { + margin-bottom: 1.5rem; + + h6 { + margin: 0 0 0.75rem 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + ul { + list-style: none; + padding: 0; + margin: 0; + + li { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; + font-size: 0.875rem; + color: nb-theme(text-hint-color); + + nb-icon { + font-size: 1.25rem; + } + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/pricing-regions/pricing-regions.component.ts b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.ts new file mode 100644 index 00000000..a06c50a5 --- /dev/null +++ b/src/app/pages/dashboard/pricing-regions/pricing-regions.component.ts @@ -0,0 +1,52 @@ +import { Component } from '@angular/core'; + +interface PricingTier { + region: string; + flag: string; + currency: string; + basePrice: number; + perVerification: number; + volumeDiscount: string; +} + +@Component({ + selector: 'ngx-pricing-regions', + styleUrls: ['./pricing-regions.component.scss'], + templateUrl: './pricing-regions.component.html', +}) +export class PricingRegionsComponent { + + pricingTiers: PricingTier[] = [ + { + region: 'APAC', + flag: '🌏', + currency: 'USD', + basePrice: 299, + perVerification: 0.15, + volumeDiscount: '10% off 100k+' + }, + { + region: 'MENA', + flag: '🌍', + currency: 'USD', + basePrice: 349, + perVerification: 0.18, + volumeDiscount: '15% off 150k+' + }, + { + region: 'Europe', + flag: '🇪🇺', + currency: 'EUR', + basePrice: 279, + perVerification: 0.14, + volumeDiscount: '12% off 100k+' + } + ]; + + features = [ + 'Real-time API access', + 'Batch processing', + '99.9% uptime SLA', + '24/7 support' + ]; +} \ No newline at end of file diff --git a/src/app/pages/dashboard/recent-verifications/recent-verifications.component.html b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.html new file mode 100644 index 00000000..ef9fffa6 --- /dev/null +++ b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.html @@ -0,0 +1,41 @@ + + +
Recent Verifications
+ +
+ + + +
+
+
+ {{ getCountryFlag(verification.country) }} + {{ verification.country }} +
+ {{ getTimeAgo(verification.timestamp) }} +
+ +
+
+ {{ verification.type }} + {{ verification.id }} +
+
+ + + + +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/app/pages/dashboard/recent-verifications/recent-verifications.component.scss b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.scss new file mode 100644 index 00000000..aef415d9 --- /dev/null +++ b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.scss @@ -0,0 +1,101 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card { + height: 450px; + } + + nb-card-header { + display: flex; + justify-content: space-between; + align-items: center; + + h6 { + margin: 0; + font-weight: 600; + } + } + + nb-card-body { + padding: 0; + overflow-y: auto; + } + + nb-list { + margin: 0; + } + + nb-list-item { + padding: 0; + border: none; + + &:hover { + background-color: nb-theme(background-basic-color-2); + } + } + + .verification-item { + padding: 1rem; + border-bottom: 1px solid nb-theme(border-basic-color-3); + } + + .verification-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; + + .country-info { + display: flex; + align-items: center; + gap: 0.5rem; + + .country-flag { + font-size: 1.25rem; + } + + .country-name { + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + + .timestamp { + font-size: 0.75rem; + color: nb-theme(text-hint-color); + } + } + + .verification-details { + .type-and-id { + display: flex; + justify-content: space-between; + margin-bottom: 0.5rem; + + .type { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + } + + .id { + font-size: 0.75rem; + color: nb-theme(text-hint-color); + font-family: monospace; + } + } + + .score-and-status { + display: flex; + align-items: center; + gap: 0.5rem; + + nb-progress-bar { + flex: 1; + } + + nb-icon { + font-size: 1.25rem; + } + } + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/recent-verifications/recent-verifications.component.ts b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.ts new file mode 100644 index 00000000..8550efdf --- /dev/null +++ b/src/app/pages/dashboard/recent-verifications/recent-verifications.component.ts @@ -0,0 +1,126 @@ +import { Component, OnDestroy } from '@angular/core'; +import { takeWhile } from 'rxjs/operators'; +import { NbThemeService } from '@nebular/theme'; + +interface Verification { + id: string; + country: string; + timestamp: Date; + matchScore: number; + status: 'verified' | 'partial' | 'failed'; + type: string; +} + +@Component({ + selector: 'ngx-recent-verifications', + styleUrls: ['./recent-verifications.component.scss'], + templateUrl: './recent-verifications.component.html', +}) +export class RecentVerificationsComponent implements OnDestroy { + + private alive = true; + + verifications: Verification[] = [ + { + id: 'VER-2024-001', + country: 'Australia', + timestamp: new Date(Date.now() - 120000), + matchScore: 95, + status: 'verified', + type: 'Identity Check' + }, + { + id: 'VER-2024-002', + country: 'Indonesia', + timestamp: new Date(Date.now() - 300000), + matchScore: 78, + status: 'partial', + type: 'Address Verification' + }, + { + id: 'VER-2024-003', + country: 'Japan', + timestamp: new Date(Date.now() - 600000), + matchScore: 92, + status: 'verified', + type: 'Full KYC' + }, + { + id: 'VER-2024-004', + country: 'Malaysia', + timestamp: new Date(Date.now() - 900000), + matchScore: 45, + status: 'failed', + type: 'Identity Check' + }, + { + id: 'VER-2024-005', + country: 'Australia', + timestamp: new Date(Date.now() - 1200000), + matchScore: 88, + status: 'verified', + type: 'Document Verify' + }, + { + id: 'VER-2024-006', + country: 'Indonesia', + timestamp: new Date(Date.now() - 1500000), + matchScore: 91, + status: 'verified', + type: 'Full KYC' + } + ]; + + constructor(private themeService: NbThemeService) {} + + getStatusIcon(status: string): string { + switch (status) { + case 'verified': + return 'checkmark-circle-2-outline'; + case 'partial': + return 'alert-triangle-outline'; + case 'failed': + return 'close-circle-outline'; + default: + return 'minus-circle-outline'; + } + } + + getStatusColor(status: string): string { + switch (status) { + case 'verified': + return 'success'; + case 'partial': + return 'warning'; + case 'failed': + return 'danger'; + default: + return 'basic'; + } + } + + getCountryFlag(country: string): string { + const flags = { + 'Australia': '🇦🇺', + 'Indonesia': '🇮🇩', + 'Malaysia': '🇲🇾', + 'Japan': '🇯🇵' + }; + return flags[country] || '🌏'; + } + + getTimeAgo(date: Date): string { + const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000); + if (seconds < 60) return 'just now'; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + + ngOnDestroy() { + this.alive = false; + } +} \ No newline at end of file diff --git a/src/app/pages/dashboard/status-card/status-card.component.ts b/src/app/pages/dashboard/status-card/status-card.component.ts index 16276408..82068fe0 100644 --- a/src/app/pages/dashboard/status-card/status-card.component.ts +++ b/src/app/pages/dashboard/status-card/status-card.component.ts @@ -4,7 +4,7 @@ import { Component, Input } from '@angular/core'; selector: 'ngx-status-card', styleUrls: ['./status-card.component.scss'], template: ` - +
@@ -13,14 +13,17 @@ import { Component, Input } from '@angular/core';
{{ title }}
-
{{ on ? 'ON' : 'OFF' }}
+
+ {{ value }} + {{ unitOfMeasurement }} +
`, }) export class StatusCardComponent { - @Input() title: string; @Input() type: string; - @Input() on = true; -} + @Input() value: string; + @Input() unitOfMeasurement: string; +} \ No newline at end of file diff --git a/src/app/pages/identity/identity.module.ts b/src/app/pages/identity/identity.module.ts index ae6ad750..d9904951 100644 --- a/src/app/pages/identity/identity.module.ts +++ b/src/app/pages/identity/identity.module.ts @@ -9,7 +9,8 @@ import { NbSelectModule, NbSpinnerModule, NbProgressBarModule, - NbIconModule + NbIconModule, + NbListModule } from '@nebular/theme'; import { ManualLookupComponent } from './manual-lookup/manual-lookup.component'; import { ResultsHistoryComponent } from './results-history/results-history.component'; @@ -34,7 +35,8 @@ import { ResultsHistoryComponent } from './results-history/results-history.compo NbSelectModule, NbSpinnerModule, NbProgressBarModule, - NbIconModule + NbIconModule, + NbListModule ], declarations: [ ManualLookupComponent, diff --git a/src/app/pages/identity/manual-lookup/manual-lookup.component.html b/src/app/pages/identity/manual-lookup/manual-lookup.component.html index ada2805c..8b3400b6 100644 --- a/src/app/pages/identity/manual-lookup/manual-lookup.component.html +++ b/src/app/pages/identity/manual-lookup/manual-lookup.component.html @@ -1,76 +1,150 @@
-
+
Identity Verification - Manual Lookup
-
- -
- +
+
+
+ + + + First name is required + +
+
+ +
+
+ + + + Last name is required + +
-
- -
- +
+
+
+ + + + Date of birth is required + +
+
+ +
+
+ + + Australia + Indonesia + Japan + Malaysia + + + Please select a country + +
-
- -
- +
+
+
+ + + Optional: National ID, Passport, or Driver's License +
+
+ +
+
+ + + + Please enter a valid email + +
-
- -
- - Australia - Indonesia - Japan - Malaysia - +
+
+
+ + +
+
+ +
+
+ + +
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
-
@@ -79,52 +153,105 @@
-
- +
+ -
Verification Results
+
Verification Tips
+
+ + + + + Use full legal names as they appear on official documents + + + + Ensure date format is YYYY-MM-DD + + + + Include middle names if available + + + + Double-check spelling and formatting + + + +
+ + + +
Verification Results
Overall Match Confidence
- + +
Field-level Match Breakdown
-
+
Name - {{ result.fieldMatches.name }}% + {{ result.fieldMatches.name }}%
- + +
-
-
+
+
Date of Birth - {{ result.fieldMatches.dateOfBirth }}% + {{ result.fieldMatches.dateOfBirth }}%
- + +
-
-
+
+
Address - {{ result.fieldMatches.address }}% + {{ result.fieldMatches.address }}%
- + +
-
-
+
+
Identification - {{ result.fieldMatches.identification }}% + {{ result.fieldMatches.identification }}%
- + +
+ +
+ + +
diff --git a/src/app/pages/identity/manual-lookup/manual-lookup.component.scss b/src/app/pages/identity/manual-lookup/manual-lookup.component.scss index 2fb72625..f0d98e18 100644 --- a/src/app/pages/identity/manual-lookup/manual-lookup.component.scss +++ b/src/app/pages/identity/manual-lookup/manual-lookup.component.scss @@ -1,7 +1,108 @@ -.field-match-item { - margin-bottom: 15px; -} +@import '../../../@theme/styles/themes'; -.match-result { - margin-bottom: 20px; +@include nb-install-component() { + .form-group { + margin-bottom: 1.5rem; + + .label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: nb-theme(text-basic-color); + font-size: 0.875rem; + } + + .error-message { + display: block; + margin-top: 0.25rem; + font-size: 0.75rem; + color: nb-theme(color-danger-default); + } + + .hint-text { + display: block; + margin-top: 0.25rem; + font-size: 0.75rem; + color: nb-theme(text-hint-color); + } + } + + .info-card { + nb-list { + margin: 0; + } + + nb-list-item { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem 0; + border: none; + + nb-icon { + flex-shrink: 0; + font-size: 1.25rem; + margin-top: 0.125rem; + } + + span { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + line-height: 1.5; + } + } + } + + .result-card { + .match-result { + margin-bottom: 2rem; + padding: 1rem; + background: nb-theme(background-basic-color-2); + border-radius: nb-theme(card-border-radius); + + h6 { + margin: 0 0 1rem 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + + .field-matches { + h6 { + margin: 0 0 1rem 0; + font-weight: 600; + color: nb-theme(text-basic-color); + } + + .field-match-item { + margin-bottom: 1rem; + + .match-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; + + span { + font-size: 0.875rem; + color: nb-theme(text-hint-color); + } + + .match-value { + font-weight: 600; + color: nb-theme(text-basic-color); + } + } + } + } + + .action-buttons { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid nb-theme(border-basic-color-3); + } + } } \ No newline at end of file diff --git a/src/app/pages/identity/manual-lookup/manual-lookup.component.ts b/src/app/pages/identity/manual-lookup/manual-lookup.component.ts index d0e8fa54..218f1772 100644 --- a/src/app/pages/identity/manual-lookup/manual-lookup.component.ts +++ b/src/app/pages/identity/manual-lookup/manual-lookup.component.ts @@ -1,17 +1,21 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'ngx-manual-lookup', templateUrl: './manual-lookup.component.html', styleUrls: ['./manual-lookup.component.scss'] }) -export class ManualLookupComponent { +export class ManualLookupComponent implements OnInit { identityForm: FormGroup; isSubmitting = false; result = null; - constructor(private fb: FormBuilder) { + constructor( + private fb: FormBuilder, + private route: ActivatedRoute + ) { this.identityForm = this.fb.group({ firstName: ['', Validators.required], lastName: ['', Validators.required], @@ -24,10 +28,21 @@ export class ManualLookupComponent { }); } + ngOnInit(): void { + // Check if country is passed as query parameter + this.route.queryParams.subscribe(params => { + if (params['country']) { + this.identityForm.patchValue({ + country: params['country'] + }); + } + }); + } + onSubmit() { if (this.identityForm.valid) { this.isSubmitting = true; - // Here you would call your identity verification service + // Simulate API call setTimeout(() => { this.isSubmitting = false; this.result = { @@ -40,6 +55,22 @@ export class ManualLookupComponent { } }; }, 2000); + } else { + // Mark all fields as touched to show validation errors + Object.keys(this.identityForm.controls).forEach(key => { + this.identityForm.get(key).markAsTouched(); + }); } } + + resetForm() { + this.identityForm.reset(); + this.result = null; + } + + getMatchStatus(score: number): string { + if (score >= 80) return 'success'; + if (score >= 60) return 'warning'; + return 'danger'; + } } \ No newline at end of file diff --git a/src/index.html b/src/index.html index 5192560c..476299ec 100644 --- a/src/index.html +++ b/src/index.html @@ -2,14 +2,15 @@ - ngx-admin Demo Application + IdentityPulse - Identity Verification Platform - + + Loading... @@ -25,4 +26,4 @@
- + \ No newline at end of file