mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-12-16 07:30:12 +01:00
Merge c264b0a79a into dc6a442704
This commit is contained in:
commit
e28c592325
64 changed files with 21560 additions and 11892 deletions
|
|
@ -187,5 +187,8 @@
|
|||
"@angular-eslint/schematics:library": {
|
||||
"setParserOptionsProject": true
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29599
package-lock.json
generated
29599
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "ngx-admin",
|
||||
"name": "identity-pulse",
|
||||
"version": "11.0.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
|
@ -62,7 +62,6 @@
|
|||
"ng2-completer": "^9.0.1",
|
||||
"ng2-smart-table": "^1.6.0",
|
||||
"ngx-echarts": "^4.2.2",
|
||||
"node-sass": "^4.14.1",
|
||||
"normalize.css": "6.0.0",
|
||||
"pace-js": "1.0.2",
|
||||
"roboto-fontface": "0.8.0",
|
||||
|
|
@ -110,6 +109,7 @@
|
|||
"npm-run-all": "4.0.2",
|
||||
"protractor": "~7.0.0",
|
||||
"rimraf": "2.6.1",
|
||||
"sass": "^1.89.0",
|
||||
"stylelint": "7.13.0",
|
||||
"ts-node": "3.2.2",
|
||||
"tslint": "~6.1.0",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { NbThemeService } from '@nebular/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-footer',
|
||||
styleUrls: ['./footer.component.scss'],
|
||||
template: `
|
||||
<span class="created-by">
|
||||
Created with ♥ by <b><a href="https://akveo.page.link/8V2f" target="_blank">Akveo</a></b> 2019
|
||||
Powered by <b><a href="https://marketsoft.com.au" target="_blank">IdentityPulse</a></b> © 2025
|
||||
</span>
|
||||
<div class="socials">
|
||||
<a href="#" target="_blank" class="ion ion-social-github"></a>
|
||||
|
|
@ -16,4 +17,5 @@ import { Component } from '@angular/core';
|
|||
`,
|
||||
})
|
||||
export class FooterComponent {
|
||||
}
|
||||
constructor(private themeService: NbThemeService) {}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<a (click)="toggleSidebar()" href="#" class="sidebar-toggle">
|
||||
<nb-icon icon="menu-2-outline"></nb-icon>
|
||||
</a>
|
||||
<a class="logo" href="#" (click)="navigateHome()">ngx-<span>admin</span></a>
|
||||
<a class="logo" href="#" (click)="navigateHome()">Identity<span>Pulse</span></a>
|
||||
</div>
|
||||
<nb-select [selected]="currentTheme" (selectedChange)="changeTheme($event)" status="primary">
|
||||
<nb-option *ngFor="let theme of themes" [value]="theme.value"> {{ theme.name }}</nb-option>
|
||||
|
|
|
|||
|
|
@ -34,9 +34,13 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||
value: 'corporate',
|
||||
name: 'Corporate',
|
||||
},
|
||||
{
|
||||
value: 'marketsoft',
|
||||
name: 'Marketsoft',
|
||||
},
|
||||
];
|
||||
|
||||
currentTheme = 'default';
|
||||
currentTheme = 'marketsoft';
|
||||
|
||||
userMenu = [ { title: 'Profile' }, { title: 'Log out' } ];
|
||||
|
||||
|
|
@ -91,4 +95,4 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
|||
this.menuService.navigateHome();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=swap');
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
|
||||
|
||||
// themes - our custom or/and out of the box themes
|
||||
@import 'themes';
|
||||
|
|
|
|||
171
src/app/@theme/styles/theme.marketsoft.ts
Normal file
171
src/app/@theme/styles/theme.marketsoft.ts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
import { NbJSThemeOptions, DEFAULT_THEME as baseTheme } from '@nebular/theme';
|
||||
|
||||
const baseThemeVariables = baseTheme.variables;
|
||||
|
||||
export const MARKETSOFT_THEME = {
|
||||
name: 'marketsoft',
|
||||
base: 'default',
|
||||
variables: {
|
||||
// Based on the Marketsoft brand colors from the provided brand guide
|
||||
// Primary color - Marketsoft red
|
||||
temperature: {
|
||||
arcFill: ['#D6001C', '#D6001C', '#D6001C', '#D6001C', '#D6001C'],
|
||||
arcEmpty: baseThemeVariables.bg2,
|
||||
thumbBg: '#FFFFFF',
|
||||
thumbBorder: '#D6001C',
|
||||
},
|
||||
|
||||
// Override with Marketsoft brand colors
|
||||
primary: '#D6001C', // Marketsoft red
|
||||
success: '#2a2e40', // Marketsoft blue
|
||||
info: '#2a2e40', // Marketsoft blue
|
||||
warning: '#D6001C', // Marketsoft red
|
||||
danger: '#D6001C', // Marketsoft red
|
||||
|
||||
// Background colors
|
||||
bg3: '#f7f9fc',
|
||||
bg4: '#edf1f7',
|
||||
|
||||
solar: {
|
||||
gradientLeft: '#D6001C',
|
||||
gradientRight: '#D6001C',
|
||||
shadowColor: 'rgba(0, 0, 0, 0)',
|
||||
secondSeriesFill: baseThemeVariables.bg2,
|
||||
radius: ['70%', '90%'],
|
||||
},
|
||||
|
||||
traffic: {
|
||||
tooltipBg: baseThemeVariables.bg,
|
||||
tooltipBorderColor: baseThemeVariables.border2,
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 4px 16px;',
|
||||
tooltipTextColor: baseThemeVariables.fgText,
|
||||
tooltipFontWeight: 'normal',
|
||||
|
||||
yAxisSplitLine: baseThemeVariables.separator,
|
||||
|
||||
lineBg: '#D6001C',
|
||||
lineShadowBlur: '0',
|
||||
itemColor: '#D6001C',
|
||||
itemBorderColor: '#D6001C',
|
||||
itemEmphasisBorderColor: '#D6001C',
|
||||
shadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
shadowLineShadow: 'rgba(0, 0, 0, 0)',
|
||||
gradFrom: baseThemeVariables.bg,
|
||||
gradTo: baseThemeVariables.bg,
|
||||
},
|
||||
|
||||
electricity: {
|
||||
tooltipBg: baseThemeVariables.bg,
|
||||
tooltipLineColor: baseThemeVariables.fgText,
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: baseThemeVariables.border2,
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: baseThemeVariables.fgText,
|
||||
tooltipFontWeight: 'normal',
|
||||
|
||||
axisLineColor: baseThemeVariables.border3,
|
||||
xAxisTextColor: baseThemeVariables.fg,
|
||||
yAxisSplitLine: baseThemeVariables.separator,
|
||||
|
||||
itemBorderColor: '#D6001C',
|
||||
lineStyle: 'solid',
|
||||
lineWidth: '4',
|
||||
lineGradFrom: '#D6001C',
|
||||
lineGradTo: '#D6001C',
|
||||
lineShadow: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
areaGradFrom: 'rgba(214, 0, 28, 0.2)',
|
||||
areaGradTo: 'rgba(214, 0, 28, 0)',
|
||||
shadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
bubbleMap: {
|
||||
titleColor: baseThemeVariables.fgText,
|
||||
areaColor: baseThemeVariables.bg4,
|
||||
areaHoverColor: baseThemeVariables.fgHighlight,
|
||||
areaBorderColor: baseThemeVariables.border5,
|
||||
},
|
||||
|
||||
echarts: {
|
||||
bg: baseThemeVariables.bg,
|
||||
textColor: baseThemeVariables.fgText,
|
||||
axisLineColor: baseThemeVariables.fgText,
|
||||
splitLineColor: baseThemeVariables.separator,
|
||||
itemHoverShadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
tooltipBackgroundColor: '#D6001C',
|
||||
areaOpacity: '0.7',
|
||||
},
|
||||
|
||||
chartjs: {
|
||||
axisLineColor: baseThemeVariables.separator,
|
||||
textColor: baseThemeVariables.fgText,
|
||||
},
|
||||
|
||||
// Additional styling for specific components
|
||||
orders: {
|
||||
tooltipBg: baseThemeVariables.bg,
|
||||
tooltipLineColor: 'rgba(0, 0, 0, 0)',
|
||||
tooltipLineWidth: '0',
|
||||
tooltipBorderColor: baseThemeVariables.border2,
|
||||
tooltipExtraCss: 'border-radius: 10px; padding: 8px 24px;',
|
||||
tooltipTextColor: baseThemeVariables.fgText,
|
||||
tooltipFontWeight: 'normal',
|
||||
tooltipFontSize: '20',
|
||||
|
||||
axisLineColor: baseThemeVariables.border4,
|
||||
axisFontSize: '16',
|
||||
axisTextColor: baseThemeVariables.fg,
|
||||
yAxisSplitLine: baseThemeVariables.separator,
|
||||
|
||||
itemBorderColor: '#D6001C',
|
||||
lineStyle: 'solid',
|
||||
lineWidth: '4',
|
||||
|
||||
// first line
|
||||
firstAreaGradFrom: 'rgba(42, 46, 64, 0.2)',
|
||||
firstAreaGradTo: 'rgba(42, 46, 64, 0)',
|
||||
firstShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// second line
|
||||
secondLineGradFrom: '#D6001C',
|
||||
secondLineGradTo: '#D6001C',
|
||||
|
||||
secondAreaGradFrom: 'rgba(214, 0, 28, 0.2)',
|
||||
secondAreaGradTo: 'rgba(214, 0, 28, 0)',
|
||||
secondShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
// third line
|
||||
thirdLineGradFrom: '#2a2e40',
|
||||
thirdLineGradTo: '#2a2e40',
|
||||
|
||||
thirdAreaGradFrom: 'rgba(42, 46, 64, 0.2)',
|
||||
thirdAreaGradTo: 'rgba(42, 46, 64, 0)',
|
||||
thirdShadowLineDarkBg: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
|
||||
// Setting up the Marketsoft specific colors for visuals
|
||||
visitorsPieLegend: {
|
||||
firstSection: '#D6001C',
|
||||
secondSection: '#2a2e40',
|
||||
},
|
||||
|
||||
earningPie: {
|
||||
radius: ['65%', '100%'],
|
||||
center: ['50%', '50%'],
|
||||
|
||||
fontSize: '22',
|
||||
|
||||
firstPieGradientLeft: '#D6001C',
|
||||
firstPieGradientRight: '#D6001C',
|
||||
firstPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
secondPieGradientLeft: '#2a2e40',
|
||||
secondPieGradientRight: '#2a2e40',
|
||||
secondPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
|
||||
thirdPieGradientLeft: '#ffffff',
|
||||
thirdPieGradientRight: '#ffffff',
|
||||
thirdPieShadowColor: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
},
|
||||
} as NbJSThemeOptions;
|
||||
|
|
@ -86,3 +86,67 @@ $nb-themes: nb-register-theme((
|
|||
slide-out-shadow-color: 0 4px 14px 0 #8f9bb3,
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 #8f9bb3,
|
||||
), dark, dark);
|
||||
|
||||
// Add the new Marketsoft theme
|
||||
$nb-themes: nb-register-theme((
|
||||
layout-padding-top: 2.25rem,
|
||||
|
||||
menu-item-icon-margin: 0 0.5rem 0 0,
|
||||
|
||||
card-height-tiny: 13.5rem,
|
||||
card-height-small: 21.1875rem,
|
||||
card-height-medium: 28.875rem,
|
||||
card-height-large: 36.5625rem,
|
||||
card-height-giant: 44.25rem,
|
||||
card-margin-bottom: 1.875rem,
|
||||
card-header-with-select-padding-top: 0.5rem,
|
||||
card-header-with-select-padding-bottom: 0.5rem,
|
||||
|
||||
select-min-width: 6rem,
|
||||
|
||||
// Marketsoft brand colors
|
||||
color-primary-100: #ffe2e5,
|
||||
color-primary-200: #ffb2b9,
|
||||
color-primary-300: #ff7e8c,
|
||||
color-primary-400: #ff4d5f,
|
||||
color-primary-500: #D6001C, // Main Marketsoft red
|
||||
color-primary-600: #ad0017,
|
||||
color-primary-700: #840012,
|
||||
color-primary-800: #5b000c,
|
||||
color-primary-900: #320007,
|
||||
|
||||
// Custom background for the Marketsoft theme
|
||||
background-basic-color-1: #ffffff,
|
||||
background-basic-color-2: #f7f9fc,
|
||||
background-basic-color-3: #edf1f7,
|
||||
background-basic-color-4: #e4e9f2,
|
||||
|
||||
// Text colors
|
||||
text-basic-color: #2a2e40, // Marketsoft dark blue
|
||||
text-primary-color: #D6001C, // Marketsoft red
|
||||
|
||||
// Override some menu colors
|
||||
menu-item-hover-text-color: #D6001C,
|
||||
menu-item-active-text-color: #D6001C,
|
||||
menu-item-active-background-color: #ffe2e5,
|
||||
menu-item-icon-color: #D6001C,
|
||||
|
||||
// Set the slide-out colors (sidebar)
|
||||
slide-out-background: #ffffff,
|
||||
slide-out-shadow-color: 0 4px 14px 0 rgba(214, 0, 28, 0.1),
|
||||
slide-out-shadow-color-rtl: 0 4px 14px 0 rgba(214, 0, 28, 0.1),
|
||||
|
||||
// Button colors
|
||||
button-filled-primary-background-color: #D6001C,
|
||||
button-filled-primary-border-color: #D6001C,
|
||||
button-filled-primary-hover-background-color: #ad0017,
|
||||
button-filled-primary-hover-border-color: #ad0017,
|
||||
|
||||
// Border radius
|
||||
card-border-radius: 0.375rem,
|
||||
border-radius: 0.375rem,
|
||||
|
||||
// Typography settings - use Roboto as specified in the brand guide
|
||||
font-family-primary: 'Roboto, sans-serif',
|
||||
font-family-secondary: 'Roboto, sans-serif',
|
||||
), marketsoft, default);
|
||||
|
|
@ -38,6 +38,7 @@ import { DEFAULT_THEME } from './styles/theme.default';
|
|||
import { COSMIC_THEME } from './styles/theme.cosmic';
|
||||
import { CORPORATE_THEME } from './styles/theme.corporate';
|
||||
import { DARK_THEME } from './styles/theme.dark';
|
||||
import { MARKETSOFT_THEME } from './styles/theme.marketsoft'; // Import the new theme
|
||||
|
||||
const NB_MODULES = [
|
||||
NbLayoutModule,
|
||||
|
|
@ -82,11 +83,11 @@ export class ThemeModule {
|
|||
providers: [
|
||||
...NbThemeModule.forRoot(
|
||||
{
|
||||
name: 'default',
|
||||
name: 'marketsoft', // Set marketsoft as the default theme
|
||||
},
|
||||
[ DEFAULT_THEME, COSMIC_THEME, CORPORATE_THEME, DARK_THEME ],
|
||||
[ DEFAULT_THEME, COSMIC_THEME, CORPORATE_THEME, DARK_THEME, MARKETSOFT_THEME ], // Add MARKETSOFT_THEME to the themes array
|
||||
).providers,
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/app/pages/account/account.module.ts
Normal file
33
src/app/pages/account/account.module.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { NbCardModule, NbInputModule, NbButtonModule, NbIconModule } from '@nebular/theme';
|
||||
import { ProfileComponent } from './profile/profile.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
NbCardModule,
|
||||
NbInputModule,
|
||||
NbButtonModule,
|
||||
NbIconModule,
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: 'profile',
|
||||
component: ProfileComponent,
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent,
|
||||
},
|
||||
]),
|
||||
],
|
||||
declarations: [
|
||||
ProfileComponent,
|
||||
SettingsComponent,
|
||||
],
|
||||
})
|
||||
export class AccountModule { }
|
||||
74
src/app/pages/account/profile/profile.component.html
Normal file
74
src/app/pages/account/profile/profile.component.html
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h5>Your Profile</h5>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="firstName">First Name</label>
|
||||
<input nbInput fullWidth id="firstName" formControlName="firstName" placeholder="First Name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="lastName">Last Name</label>
|
||||
<input nbInput fullWidth id="lastName" formControlName="lastName" placeholder="Last Name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="email">Email</label>
|
||||
<input nbInput fullWidth id="email" formControlName="email" placeholder="Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="phone">Phone</label>
|
||||
<input nbInput fullWidth id="phone" formControlName="phone" placeholder="Phone Number">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="company">Company</label>
|
||||
<input nbInput fullWidth id="company" formControlName="company" placeholder="Company">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="label" for="position">Position</label>
|
||||
<input nbInput fullWidth id="position" formControlName="position" placeholder="Position">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="form-group">
|
||||
<label class="label" for="address">Address</label>
|
||||
<input nbInput fullWidth id="address" formControlName="address" placeholder="Address">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-center">
|
||||
<button nbButton status="primary" [disabled]="profileForm.invalid">
|
||||
<nb-icon icon="save-outline"></nb-icon> Update Profile
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
9
src/app/pages/account/profile/profile.component.scss
Normal file
9
src/app/pages/account/profile/profile.component.scss
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
32
src/app/pages/account/profile/profile.component.ts
Normal file
32
src/app/pages/account/profile/profile.component.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-profile',
|
||||
templateUrl: './profile.component.html',
|
||||
styleUrls: ['./profile.component.scss']
|
||||
})
|
||||
export class ProfileComponent implements OnInit {
|
||||
profileForm: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.profileForm = this.fb.group({
|
||||
firstName: ['John', Validators.required],
|
||||
lastName: ['Doe', Validators.required],
|
||||
email: ['john.doe@example.com', [Validators.required, Validators.email]],
|
||||
company: ['Acme Corp', Validators.required],
|
||||
position: ['Product Manager', Validators.required],
|
||||
phone: ['+1 (555) 123-4567', Validators.required],
|
||||
address: ['123 Main St, New York, NY 10001', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.profileForm.valid) {
|
||||
// Handle profile update logic
|
||||
console.log('Profile updated:', this.profileForm.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/app/pages/account/settings/settings.component.html
Normal file
101
src/app/pages/account/settings/settings.component.html
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h5>Account Settings</h5>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<form [formGroup]="settingsForm" (ngSubmit)="onSubmit()">
|
||||
<h6 class="section-title">Notifications</h6>
|
||||
<div class="settings-section">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="emailNotifications" formControlName="emailNotifications">
|
||||
<label class="custom-control-label" for="emailNotifications">Email Notifications</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">Receive verification results and updates via email</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="smsNotifications" formControlName="smsNotifications">
|
||||
<label class="custom-control-label" for="smsNotifications">SMS Notifications</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">Receive verification results and updates via SMS</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="section-title">Security</h6>
|
||||
<div class="settings-section">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="twoFactorAuth" formControlName="twoFactorAuth">
|
||||
<label class="custom-control-label" for="twoFactorAuth">Two-Factor Authentication</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">Enable 2FA for additional security</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dataRetention">Data Retention</label>
|
||||
<select class="form-control" id="dataRetention" formControlName="dataRetention">
|
||||
<option value="7days">7 Days</option>
|
||||
<option value="30days">30 Days</option>
|
||||
<option value="90days">90 Days</option>
|
||||
<option value="1year">1 Year</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">How long to keep your verification results</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="section-title">Appearance</h6>
|
||||
<div class="settings-section">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="theme">Theme</label>
|
||||
<select class="form-control" id="theme" formControlName="theme">
|
||||
<option value="default">Default</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="cosmic">Cosmic</option>
|
||||
<option value="corporate">Corporate</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="language">Language</label>
|
||||
<select class="form-control" id="language" formControlName="language">
|
||||
<option value="en">English</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="de">German</option>
|
||||
<option value="zh">Chinese</option>
|
||||
<option value="ja">Japanese</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-center">
|
||||
<button nbButton status="primary">
|
||||
<nb-icon icon="save-outline"></nb-icon> Save Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
19
src/app/pages/account/settings/settings.component.scss
Normal file
19
src/app/pages/account/settings/settings.component.scss
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
.section-title {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #edf1f7;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.custom-control-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
31
src/app/pages/account/settings/settings.component.ts
Normal file
31
src/app/pages/account/settings/settings.component.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss']
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
settingsForm: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.settingsForm = this.fb.group({
|
||||
emailNotifications: [true],
|
||||
smsNotifications: [false],
|
||||
twoFactorAuth: [true],
|
||||
dataRetention: ['30days'],
|
||||
theme: ['default'],
|
||||
language: ['en'],
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.settingsForm.valid) {
|
||||
// Handle settings update logic
|
||||
console.log('Settings updated:', this.settingsForm.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/app/pages/countries/countries.component.html
Normal file
119
src/app/pages/countries/countries.component.html
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h5>Supported Countries & Regions</h5>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<p class="subtitle">IdentityPulse provides comprehensive identity verification coverage across Asia-Pacific and expanding into the Middle East.</p>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h6 class="section-title">Active Countries</h6>
|
||||
</div>
|
||||
<div class="col-lg-6 col-xl-4" *ngFor="let country of activeCountries">
|
||||
<nb-card class="country-card" [class.active]="country.status === 'active'">
|
||||
<nb-card-header>
|
||||
<div class="country-header">
|
||||
<div class="country-title">
|
||||
<span class="flag">{{ country.flag }}</span>
|
||||
<h6>{{ country.name }}</h6>
|
||||
</div>
|
||||
<nb-badge [text]="country.status" status="success"></nb-badge>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="country-stats">
|
||||
<div class="stat">
|
||||
<span class="label">Coverage</span>
|
||||
<span class="value">{{ country.coverage }}%</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="label">Population</span>
|
||||
<span class="value">{{ country.population }}</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="label">Databases</span>
|
||||
<span class="value">{{ country.databases }}</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="label">Updates</span>
|
||||
<span class="value">{{ country.updateFrequency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features">
|
||||
<h6>Available Features</h6>
|
||||
<div class="feature-tags">
|
||||
<span class="tag" *ngFor="let feature of country.features">{{ feature }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="regulations">
|
||||
<h6>Compliance</h6>
|
||||
<div class="regulation-tags">
|
||||
<span class="tag" *ngFor="let reg of country.regulations">{{ reg }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button nbButton fullWidth status="primary" routerLink="/pages/identity/manual-lookup"
|
||||
[queryParams]="{country: country.name.toLowerCase().replace(' ', '-')}">
|
||||
<nb-icon icon="search-outline"></nb-icon>
|
||||
Verify in {{ country.name }}
|
||||
</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" *ngIf="comingSoonCountries.length > 0">
|
||||
<div class="col-12">
|
||||
<h6 class="section-title">Coming Soon</h6>
|
||||
</div>
|
||||
<div class="col-lg-6 col-xl-4" *ngFor="let country of comingSoonCountries">
|
||||
<nb-card class="country-card coming-soon">
|
||||
<nb-card-header>
|
||||
<div class="country-header">
|
||||
<div class="country-title">
|
||||
<span class="flag">{{ country.flag }}</span>
|
||||
<h6>{{ country.name }}</h6>
|
||||
</div>
|
||||
<nb-badge text="coming soon" status="warning"></nb-badge>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="country-stats">
|
||||
<div class="stat">
|
||||
<span class="label">Population</span>
|
||||
<span class="value">{{ country.population }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features">
|
||||
<h6>Planned Features</h6>
|
||||
<div class="feature-tags">
|
||||
<span class="tag" *ngFor="let feature of country.features">{{ feature }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="regulations">
|
||||
<h6>Target Compliance</h6>
|
||||
<div class="regulation-tags">
|
||||
<span class="tag" *ngFor="let reg of country.regulations">{{ reg }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button nbButton fullWidth status="basic" disabled>
|
||||
<nb-icon icon="clock-outline"></nb-icon>
|
||||
Coming Soon
|
||||
</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
108
src/app/pages/countries/countries.component.scss
Normal file
108
src/app/pages/countries/countries.component.scss
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
@import '../../@theme/styles/themes';
|
||||
|
||||
@include nb-install-component() {
|
||||
.subtitle {
|
||||
color: nb-theme(text-hint-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/app/pages/countries/countries.component.ts
Normal file
82
src/app/pages/countries/countries.component.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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 {
|
||||
|
||||
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',
|
||||
regulations: ['UU PDP', 'OJK Regulations'],
|
||||
features: ['Identity Verification', 'Address Validation', 'Phone Verification', 'KTP Validation']
|
||||
},
|
||||
{
|
||||
name: 'Malaysia',
|
||||
flag: '🇲🇾',
|
||||
status: 'active',
|
||||
coverage: 78,
|
||||
population: '32.4M',
|
||||
databases: 3,
|
||||
updateFrequency: 'Weekly',
|
||||
regulations: ['PDPA 2010', 'AMLA'],
|
||||
features: ['MyKad Verification', 'Address Check', 'Phone Validation', 'Business Registry']
|
||||
},
|
||||
{
|
||||
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']
|
||||
}
|
||||
];
|
||||
|
||||
activeCountries = this.countries.filter(c => c.status === 'active');
|
||||
comingSoonCountries = this.countries.filter(c => c.status === 'coming-soon');
|
||||
}
|
||||
30
src/app/pages/countries/countries.module.ts
Normal file
30
src/app/pages/countries/countries.module.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import {
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
NbIconModule,
|
||||
NbBadgeModule
|
||||
} from '@nebular/theme';
|
||||
import { CountriesComponent } from './countries.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: CountriesComponent,
|
||||
},
|
||||
]),
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
NbIconModule,
|
||||
NbBadgeModule
|
||||
],
|
||||
declarations: [
|
||||
CountriesComponent,
|
||||
],
|
||||
})
|
||||
export class CountriesModule { }
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h6>Trusted by Industry Leaders</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="clients-grid">
|
||||
<div class="client-card" *ngFor="let client of clients">
|
||||
<div class="client-logo">
|
||||
<div class="logo-placeholder">
|
||||
{{ client.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="client-info">
|
||||
<h6 class="client-name">{{ client.name }}</h6>
|
||||
<span class="client-industry">{{ client.industry }}</span>
|
||||
<p class="client-description">{{ client.description }}</p>
|
||||
<div class="verification-count">
|
||||
<nb-icon icon="checkmark-circle-2-outline" pack="eva"></nb-icon>
|
||||
<span>{{ client.verifications }} verifications</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="showcase-footer">
|
||||
<div class="trust-metrics">
|
||||
<div class="metric">
|
||||
<span class="metric-value">50+</span>
|
||||
<span class="metric-label">Enterprise Clients</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-value">15M+</span>
|
||||
<span class="metric-label">Monthly Verifications</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-value">99.9%</span>
|
||||
<span class="metric-label">Uptime SLA</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h6>Compliance & Data Residency</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="compliance-list">
|
||||
<div class="compliance-item" *ngFor="let item of complianceItems">
|
||||
<div class="item-header">
|
||||
<span class="region">{{ item.region }}</span>
|
||||
<nb-icon
|
||||
[icon]="getStatusIcon(item.status)"
|
||||
[status]="getStatusColor(item.status)"
|
||||
pack="eva">
|
||||
</nb-icon>
|
||||
</div>
|
||||
|
||||
<div class="regulations">
|
||||
<span class="regulation" *ngFor="let reg of item.regulations">
|
||||
{{ reg }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="hosting-model">
|
||||
<nb-icon icon="hard-drive-outline" pack="eva"></nb-icon>
|
||||
<span>{{ item.hostingModel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="certifications">
|
||||
<h6>Certifications</h6>
|
||||
<div class="cert-badges">
|
||||
<div class="badge" *ngFor="let cert of certifications">
|
||||
<nb-icon [icon]="cert.icon" pack="eva" status="primary"></nb-icon>
|
||||
<span>{{ cert.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<nb-card size="large">
|
||||
<nb-card-header>
|
||||
<span>Country Coverage & Availability</span>
|
||||
<nb-select [(selected)]="selectedRegion" class="region-select">
|
||||
<nb-option value="all">All Regions</nb-option>
|
||||
<nb-option value="apac">APAC</nb-option>
|
||||
<nb-option value="mena">MENA</nb-option>
|
||||
<nb-option value="europe">Europe</nb-option>
|
||||
</nb-select>
|
||||
</nb-card-header>
|
||||
|
||||
<nb-card-body>
|
||||
<div class="country-list">
|
||||
<div class="country-item" *ngFor="let country of countries">
|
||||
<div class="country-header">
|
||||
<div class="country-info">
|
||||
<h6 class="country-name">{{ country.name }}</h6>
|
||||
<nb-icon
|
||||
[icon]="getStatusIcon(country.status)"
|
||||
[status]="getStatusColor(country.status)"
|
||||
pack="eva">
|
||||
</nb-icon>
|
||||
</div>
|
||||
<div class="country-stats">
|
||||
<span class="records">{{ country.records }} records</span>
|
||||
<span class="update-freq">{{ country.updateFrequency }} updates</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="coverage-section">
|
||||
<div class="coverage-header">
|
||||
<span class="coverage-label">Coverage</span>
|
||||
<span class="coverage-value">{{ country.coverage }}%</span>
|
||||
</div>
|
||||
<nb-progress-bar
|
||||
[value]="country.coverage"
|
||||
[status]="getCoverageStatus(country.coverage)"
|
||||
size="tiny">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,45 @@
|
|||
<div class="row">
|
||||
<div class="col-xxxl-3 col-md-6" *ngFor="let statusCard of statusCards">
|
||||
<ngx-status-card [title]="statusCard.title" [type]="statusCard.type">
|
||||
<i [ngClass]="statusCard.iconClass"></i>
|
||||
</ngx-status-card>
|
||||
<ngx-metric-card
|
||||
[title]="statusCard.title"
|
||||
[type]="statusCard.type"
|
||||
[value]="statusCard.value"
|
||||
[unitOfMeasurement]="statusCard.unitOfMeasurement"
|
||||
[iconClass]="statusCard.iconClass">
|
||||
</ngx-metric-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xxxl-3 col-xxl-4 col-lg-5 col-md-6">
|
||||
<ngx-temperature></ngx-temperature>
|
||||
<div class="col-xxxl-6 col-xxl-7 col-lg-7 col-md-12">
|
||||
<ngx-country-coverage></ngx-country-coverage>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-9 col-xxl-8 col-lg-7 col-md-6">
|
||||
<ngx-electricity></ngx-electricity>
|
||||
<div class="col-xxxl-6 col-xxl-5 col-lg-5 col-md-12">
|
||||
<ngx-client-showcase></ngx-client-showcase>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-xxxl-8 col-xl-12">
|
||||
<ngx-data-centers-map></ngx-data-centers-map>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-4 col-xxl-4 col-lg-5 col-md-6">
|
||||
<ngx-recent-verifications></ngx-recent-verifications>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xxxl-9 col-xl-12">
|
||||
<ngx-rooms></ngx-rooms>
|
||||
<div class="col-xxxl-4 col-xxl-4 col-lg-4 col-md-6">
|
||||
<ngx-pricing-regions></ngx-pricing-regions>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-3 col-xxl-4 col-lg-7 col-md-6">
|
||||
<ngx-contacts></ngx-contacts>
|
||||
<div class="col-xxxl-4 col-xxl-4 col-lg-4 col-md-6">
|
||||
<ngx-compliance-summary></ngx-compliance-summary>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-3 col-xxl-4 col-lg-5 col-md-6">
|
||||
<ngx-solar [chartValue]="solarValue"></ngx-solar>
|
||||
|
||||
<ngx-kitten></ngx-kitten>
|
||||
<div class="col-xxxl-4 col-xxl-4 col-lg-4 col-md-12">
|
||||
<ngx-differentiators></ngx-differentiators>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-3 col-xxl-4 col-md-5">
|
||||
<ngx-traffic></ngx-traffic>
|
||||
<ngx-weather></ngx-weather>
|
||||
</div>
|
||||
|
||||
<div class="col-xxxl-6 col-xxl-12 col-md-7">
|
||||
<ngx-security-cameras></ngx-security-cameras>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -13,4 +13,4 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 { }
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<div class="header-container">
|
||||
<h6>Global Identity Verification Coverage</h6>
|
||||
<div class="legend">
|
||||
<span class="legend-item">
|
||||
<span class="dot online"></span>
|
||||
Online
|
||||
</span>
|
||||
<span class="legend-item">
|
||||
<span class="dot maintenance"></span>
|
||||
Maintenance
|
||||
</span>
|
||||
<span class="legend-item">
|
||||
<span class="dot offline"></span>
|
||||
Offline
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div id="map" class="map-container"></div>
|
||||
<div class="map-stats">
|
||||
<div class="stat">
|
||||
<nb-icon icon="globe-2-outline" pack="eva"></nb-icon>
|
||||
<span>{{ getCountryCount() }} Countries</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<nb-icon icon="hard-drive-outline" pack="eva"></nb-icon>
|
||||
<span>{{ getTotalDatabases() }} Databases</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<nb-icon icon="activity-outline" pack="eva"></nb-icon>
|
||||
<span>{{ getOnlineCount() }} Online</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = `
|
||||
<div style="
|
||||
background-color: ${colors[status]};
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
"></div>
|
||||
`;
|
||||
|
||||
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 `
|
||||
<div style="padding: 10px; min-width: 200px;">
|
||||
<h6 style="margin: 0 0 10px 0; font-weight: 600;">${dc.name}</h6>
|
||||
<div style="font-size: 14px; line-height: 1.6;">
|
||||
<div><strong>City:</strong> ${dc.city}, ${dc.country}</div>
|
||||
<div><strong>Status:</strong> <span style="color: ${statusColors[dc.status]}; font-weight: 600;">${dc.status}</span></div>
|
||||
<div><strong>Databases:</strong> ${dc.databases}</div>
|
||||
<div><strong>Response Time:</strong> ${dc.responseTime}ms</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #e4e9f2; text-align: center;">
|
||||
<span style="color: #0095ff; font-size: 12px; cursor: pointer;">
|
||||
<strong>Click to query this region →</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h6>Why IdentityPulse is Different</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="differentiators-grid">
|
||||
<div class="differentiator" *ngFor="let item of differentiators">
|
||||
<div class="icon-wrapper">
|
||||
<nb-icon [icon]="item.icon" pack="eva"></nb-icon>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h6 class="title">{{ item.title }}</h6>
|
||||
<p class="description">{{ item.description }}</p>
|
||||
<span class="metric" *ngIf="item.metric">{{ item.metric }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cta-section">
|
||||
<p class="cta-text">Experience the difference with a live demo</p>
|
||||
<button nbButton status="primary" fullWidth>
|
||||
Schedule Demo
|
||||
</button>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/app/pages/dashboard/metric-card/metric-card.component.ts
Normal file
30
src/app/pages/dashboard/metric-card/metric-card.component.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-metric-card',
|
||||
styleUrls: ['./metric-card.component.scss'],
|
||||
template: `
|
||||
<nb-card>
|
||||
<div class="icon-container">
|
||||
<div class="icon status-{{ type }}">
|
||||
<i [ngClass]="iconClass"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="status-value">
|
||||
<span class="value">{{ value }}</span>
|
||||
<span class="unit">{{ unitOfMeasurement }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card>
|
||||
`,
|
||||
})
|
||||
export class MetricCardComponent {
|
||||
@Input() title: string;
|
||||
@Input() type: string;
|
||||
@Input() value: string;
|
||||
@Input() unitOfMeasurement: string;
|
||||
@Input() iconClass: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h6>Regional Pricing</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="pricing-tiers">
|
||||
<div class="tier" *ngFor="let tier of pricingTiers">
|
||||
<div class="tier-header">
|
||||
<span class="region-flag">{{ tier.flag }}</span>
|
||||
<h6 class="region-name">{{ tier.region }}</h6>
|
||||
</div>
|
||||
|
||||
<div class="pricing-details">
|
||||
<div class="base-price">
|
||||
<span class="currency">{{ tier.currency }}</span>
|
||||
<span class="price">${{ tier.basePrice }}</span>
|
||||
<span class="period">/month</span>
|
||||
</div>
|
||||
|
||||
<div class="per-verification">
|
||||
<span class="label">Per verification:</span>
|
||||
<span class="value">${{ tier.perVerification }}</span>
|
||||
</div>
|
||||
|
||||
<div class="volume-discount">
|
||||
<nb-icon icon="pricetags-outline" pack="eva"></nb-icon>
|
||||
<span>{{ tier.volumeDiscount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features">
|
||||
<h6>All plans include:</h6>
|
||||
<ul>
|
||||
<li *ngFor="let feature of features">
|
||||
<nb-icon icon="checkmark-outline" pack="eva" status="success"></nb-icon>
|
||||
{{ feature }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button nbButton fullWidth status="primary">
|
||||
Contact Sales
|
||||
</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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'
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h6>Recent Verifications</h6>
|
||||
<button nbButton ghost size="tiny" status="primary">View All</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<nb-list>
|
||||
<nb-list-item *ngFor="let verification of verifications">
|
||||
<div class="verification-item">
|
||||
<div class="verification-header">
|
||||
<div class="country-info">
|
||||
<span class="country-flag">{{ getCountryFlag(verification.country) }}</span>
|
||||
<span class="country-name">{{ verification.country }}</span>
|
||||
</div>
|
||||
<span class="timestamp">{{ getTimeAgo(verification.timestamp) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="verification-details">
|
||||
<div class="type-and-id">
|
||||
<span class="type">{{ verification.type }}</span>
|
||||
<span class="id">{{ verification.id }}</span>
|
||||
</div>
|
||||
<div class="score-and-status">
|
||||
<nb-progress-bar
|
||||
[value]="verification.matchScore"
|
||||
[status]="getStatusColor(verification.status)"
|
||||
size="tiny"
|
||||
[displayValue]="true">
|
||||
</nb-progress-bar>
|
||||
<nb-icon
|
||||
[icon]="getStatusIcon(verification.status)"
|
||||
[status]="getStatusColor(verification.status)"
|
||||
pack="eva">
|
||||
</nb-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nb-list-item>
|
||||
</nb-list>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -76,4 +76,4 @@
|
|||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import { Component, Input } from '@angular/core';
|
|||
selector: 'ngx-status-card',
|
||||
styleUrls: ['./status-card.component.scss'],
|
||||
template: `
|
||||
<nb-card (click)="on = !on" [ngClass]="{'off': !on}">
|
||||
<nb-card>
|
||||
<div class="icon-container">
|
||||
<div class="icon status-{{ type }}">
|
||||
<ng-content></ng-content>
|
||||
|
|
@ -13,14 +13,17 @@ import { Component, Input } from '@angular/core';
|
|||
|
||||
<div class="details">
|
||||
<div class="title h5">{{ title }}</div>
|
||||
<div class="status paragraph-2">{{ on ? 'ON' : 'OFF' }}</div>
|
||||
<div class="status-value">
|
||||
<span class="value h2">{{ value }}</span>
|
||||
<span class="unit">{{ unitOfMeasurement }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card>
|
||||
`,
|
||||
})
|
||||
export class StatusCardComponent {
|
||||
|
||||
@Input() title: string;
|
||||
@Input() type: string;
|
||||
@Input() on = true;
|
||||
}
|
||||
@Input() value: string;
|
||||
@Input() unitOfMeasurement: string;
|
||||
}
|
||||
46
src/app/pages/identity/identity.module.ts
Normal file
46
src/app/pages/identity/identity.module.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import {
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
NbInputModule,
|
||||
NbSelectModule,
|
||||
NbSpinnerModule,
|
||||
NbProgressBarModule,
|
||||
NbIconModule,
|
||||
NbListModule
|
||||
} from '@nebular/theme';
|
||||
import { ManualLookupComponent } from './manual-lookup/manual-lookup.component';
|
||||
import { ResultsHistoryComponent } from './results-history/results-history.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: 'manual-lookup',
|
||||
component: ManualLookupComponent,
|
||||
},
|
||||
{
|
||||
path: 'results-history',
|
||||
component: ResultsHistoryComponent,
|
||||
},
|
||||
]),
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
NbInputModule,
|
||||
NbSelectModule,
|
||||
NbSpinnerModule,
|
||||
NbProgressBarModule,
|
||||
NbIconModule,
|
||||
NbListModule
|
||||
],
|
||||
declarations: [
|
||||
ManualLookupComponent,
|
||||
ResultsHistoryComponent,
|
||||
],
|
||||
})
|
||||
export class IdentityModule { }
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
<div class="row">
|
||||
<div class="col-12 col-lg-8">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h5>Identity Verification - Manual Lookup</h5>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<form [formGroup]="identityForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="firstName" class="label">First Name *</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="firstName"
|
||||
formControlName="firstName"
|
||||
placeholder="Enter first name"
|
||||
status="basic"
|
||||
[status]="identityForm.get('firstName').invalid && identityForm.get('firstName').touched ? 'danger' : 'basic'">
|
||||
<span class="error-message" *ngIf="identityForm.get('firstName').invalid && identityForm.get('firstName').touched">
|
||||
First name is required
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="lastName" class="label">Last Name *</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="lastName"
|
||||
formControlName="lastName"
|
||||
placeholder="Enter last name"
|
||||
[status]="identityForm.get('lastName').invalid && identityForm.get('lastName').touched ? 'danger' : 'basic'">
|
||||
<span class="error-message" *ngIf="identityForm.get('lastName').invalid && identityForm.get('lastName').touched">
|
||||
Last name is required
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dateOfBirth" class="label">Date of Birth *</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="dateOfBirth"
|
||||
type="date"
|
||||
formControlName="dateOfBirth"
|
||||
placeholder="YYYY-MM-DD"
|
||||
[status]="identityForm.get('dateOfBirth').invalid && identityForm.get('dateOfBirth').touched ? 'danger' : 'basic'">
|
||||
<span class="error-message" *ngIf="identityForm.get('dateOfBirth').invalid && identityForm.get('dateOfBirth').touched">
|
||||
Date of birth is required
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="country" class="label">Country *</label>
|
||||
<nb-select fullWidth
|
||||
id="country"
|
||||
formControlName="country"
|
||||
placeholder="Select Country"
|
||||
[status]="identityForm.get('country').invalid && identityForm.get('country').touched ? 'danger' : 'basic'">
|
||||
<nb-option value="australia">Australia</nb-option>
|
||||
<nb-option value="indonesia">Indonesia</nb-option>
|
||||
<nb-option value="japan">Japan</nb-option>
|
||||
<nb-option value="malaysia">Malaysia</nb-option>
|
||||
</nb-select>
|
||||
<span class="error-message" *ngIf="identityForm.get('country').invalid && identityForm.get('country').touched">
|
||||
Please select a country
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="identificationNumber" class="label">ID Number</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="identificationNumber"
|
||||
formControlName="identificationNumber"
|
||||
placeholder="Enter identification number"
|
||||
status="basic">
|
||||
<span class="hint-text">Optional: National ID, Passport, or Driver's License</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="email" class="label">Email</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="email"
|
||||
type="email"
|
||||
formControlName="email"
|
||||
placeholder="Enter email address"
|
||||
[status]="identityForm.get('email').invalid && identityForm.get('email').touched ? 'danger' : 'basic'">
|
||||
<span class="error-message" *ngIf="identityForm.get('email').invalid && identityForm.get('email').touched">
|
||||
Please enter a valid email
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="phone" class="label">Phone Number</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="phone"
|
||||
formControlName="phone"
|
||||
placeholder="Enter phone number"
|
||||
status="basic">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="address" class="label">Address</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
id="address"
|
||||
formControlName="address"
|
||||
placeholder="Enter address"
|
||||
status="basic">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<button nbButton
|
||||
fullWidth
|
||||
status="primary"
|
||||
size="large"
|
||||
[disabled]="identityForm.invalid || isSubmitting"
|
||||
[nbSpinner]="isSubmitting"
|
||||
nbSpinnerStatus="primary"
|
||||
nbSpinnerSize="small">
|
||||
<nb-icon icon="search-outline" *ngIf="!isSubmitting"></nb-icon>
|
||||
{{ isSubmitting ? 'Verifying...' : 'Verify Identity' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-4">
|
||||
<nb-card *ngIf="!result" class="info-card">
|
||||
<nb-card-header>
|
||||
<h6>Verification Tips</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<nb-list>
|
||||
<nb-list-item>
|
||||
<nb-icon icon="checkmark-circle-outline" status="success"></nb-icon>
|
||||
<span>Use full legal names as they appear on official documents</span>
|
||||
</nb-list-item>
|
||||
<nb-list-item>
|
||||
<nb-icon icon="checkmark-circle-outline" status="success"></nb-icon>
|
||||
<span>Ensure date format is YYYY-MM-DD</span>
|
||||
</nb-list-item>
|
||||
<nb-list-item>
|
||||
<nb-icon icon="checkmark-circle-outline" status="success"></nb-icon>
|
||||
<span>Include middle names if available</span>
|
||||
</nb-list-item>
|
||||
<nb-list-item>
|
||||
<nb-icon icon="checkmark-circle-outline" status="success"></nb-icon>
|
||||
<span>Double-check spelling and formatting</span>
|
||||
</nb-list-item>
|
||||
</nb-list>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
||||
<nb-card *ngIf="result" class="result-card">
|
||||
<nb-card-header>
|
||||
<h6>Verification Results</h6>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="match-result">
|
||||
<h6>Overall Match Confidence</h6>
|
||||
<nb-progress-bar [value]="result.overallMatch"
|
||||
[status]="getMatchStatus(result.overallMatch)"
|
||||
[displayValue]="true"
|
||||
size="large">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
|
||||
<div class="field-matches mt-4">
|
||||
<h6>Field-level Match Breakdown</h6>
|
||||
|
||||
<div class="field-match-item">
|
||||
<div class="match-header">
|
||||
<span>Name</span>
|
||||
<span class="match-value">{{ result.fieldMatches.name }}%</span>
|
||||
</div>
|
||||
<nb-progress-bar [value]="result.fieldMatches.name"
|
||||
size="tiny"
|
||||
[status]="getMatchStatus(result.fieldMatches.name)">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
|
||||
<div class="field-match-item">
|
||||
<div class="match-header">
|
||||
<span>Date of Birth</span>
|
||||
<span class="match-value">{{ result.fieldMatches.dateOfBirth }}%</span>
|
||||
</div>
|
||||
<nb-progress-bar [value]="result.fieldMatches.dateOfBirth"
|
||||
size="tiny"
|
||||
[status]="getMatchStatus(result.fieldMatches.dateOfBirth)">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
|
||||
<div class="field-match-item">
|
||||
<div class="match-header">
|
||||
<span>Address</span>
|
||||
<span class="match-value">{{ result.fieldMatches.address }}%</span>
|
||||
</div>
|
||||
<nb-progress-bar [value]="result.fieldMatches.address"
|
||||
size="tiny"
|
||||
[status]="getMatchStatus(result.fieldMatches.address)">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
|
||||
<div class="field-match-item">
|
||||
<div class="match-header">
|
||||
<span>Identification</span>
|
||||
<span class="match-value">{{ result.fieldMatches.identification }}%</span>
|
||||
</div>
|
||||
<nb-progress-bar [value]="result.fieldMatches.identification"
|
||||
size="tiny"
|
||||
[status]="getMatchStatus(result.fieldMatches.identification)">
|
||||
</nb-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button nbButton fullWidth status="info" size="medium">
|
||||
<nb-icon icon="download-outline"></nb-icon>
|
||||
Download Report
|
||||
</button>
|
||||
<button nbButton fullWidth status="basic" size="medium" (click)="resetForm()">
|
||||
<nb-icon icon="refresh-outline"></nb-icon>
|
||||
New Verification
|
||||
</button>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
@import '../../../@theme/styles/themes';
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
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 implements OnInit {
|
||||
identityForm: FormGroup;
|
||||
isSubmitting = false;
|
||||
result = null;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private route: ActivatedRoute
|
||||
) {
|
||||
this.identityForm = this.fb.group({
|
||||
firstName: ['', Validators.required],
|
||||
lastName: ['', Validators.required],
|
||||
dateOfBirth: ['', Validators.required],
|
||||
country: ['', Validators.required],
|
||||
identificationNumber: [''],
|
||||
email: ['', Validators.email],
|
||||
phone: [''],
|
||||
address: [''],
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
this.isSubmitting = false;
|
||||
this.result = {
|
||||
overallMatch: 85,
|
||||
fieldMatches: {
|
||||
name: 90,
|
||||
dateOfBirth: 100,
|
||||
address: 70,
|
||||
identification: 80
|
||||
}
|
||||
};
|
||||
}, 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';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
<h5>Verification Results History</h5>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date/Time</th>
|
||||
<th>Country</th>
|
||||
<th>Match Score</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let lookup of recentLookups">
|
||||
<td>{{ lookup.date }}</td>
|
||||
<td>{{ lookup.country }}</td>
|
||||
<td>
|
||||
<nb-progress-bar [value]="lookup.score" size="tiny" [status]="getStatusClass(lookup.status)" [displayValue]="true"></nb-progress-bar>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ getStatusClass(lookup.status) }}">{{ lookup.status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button nbButton ghost size="small" status="info" title="View Details">
|
||||
<nb-icon icon="eye-outline"></nb-icon>
|
||||
</button>
|
||||
<button nbButton ghost size="small" status="primary" title="Download Report">
|
||||
<nb-icon icon="download-outline"></nb-icon>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 0.4rem 0.6rem;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background-color: #00d68f;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background-color: #ffaa00;
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background-color: #ff3d71;
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
background-color: #0095ff;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-results-history',
|
||||
templateUrl: './results-history.component.html',
|
||||
styleUrls: ['./results-history.component.scss']
|
||||
})
|
||||
export class ResultsHistoryComponent implements OnInit {
|
||||
|
||||
// Sample data for demonstration
|
||||
recentLookups = [
|
||||
{ date: '2025-05-19 10:30', country: 'Australia', score: 95, status: 'Verified' },
|
||||
{ date: '2025-05-19 09:15', country: 'Indonesia', score: 82, status: 'Verified' },
|
||||
{ date: '2025-05-18 16:45', country: 'Malaysia', score: 60, status: 'Partial Match' },
|
||||
{ date: '2025-05-18 14:20', country: 'Japan', score: 42, status: 'No Match' },
|
||||
{ date: '2025-05-17 11:30', country: 'Australia', score: 88, status: 'Verified' },
|
||||
{ date: '2025-05-17 09:45', country: 'Indonesia', score: 76, status: 'Verified' },
|
||||
];
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
getStatusClass(status: string): string {
|
||||
switch (status) {
|
||||
case 'Verified':
|
||||
return 'success';
|
||||
case 'Partial Match':
|
||||
return 'warning';
|
||||
case 'No Match':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,5 +21,4 @@ const routes: Routes = [
|
|||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class MiscellaneousRoutingModule {
|
||||
}
|
||||
export class MiscellaneousRoutingModule { }
|
||||
|
|
@ -2,9 +2,7 @@ import { Component } from '@angular/core';
|
|||
|
||||
@Component({
|
||||
selector: 'ngx-miscellaneous',
|
||||
template: `
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
})
|
||||
export class MiscellaneousComponent {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { NbButtonModule, NbCardModule } from '@nebular/theme';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ThemeModule } from '../../@theme/theme.module';
|
||||
import { NbCardModule, NbButtonModule, NbIconModule } from '@nebular/theme';
|
||||
|
||||
import { MiscellaneousRoutingModule } from './miscellaneous-routing.module';
|
||||
import { MiscellaneousComponent } from './miscellaneous.component';
|
||||
import { NotFoundComponent } from './not-found/not-found.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ThemeModule,
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
NbIconModule,
|
||||
MiscellaneousRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
|
|
@ -18,4 +21,4 @@ import { NotFoundComponent } from './not-found/not-found.component';
|
|||
NotFoundComponent,
|
||||
],
|
||||
})
|
||||
export class MiscellaneousModule { }
|
||||
export class MiscellaneousModule { }
|
||||
|
|
@ -2,246 +2,42 @@ import { NbMenuItem } from '@nebular/theme';
|
|||
|
||||
export const MENU_ITEMS: NbMenuItem[] = [
|
||||
{
|
||||
title: 'E-commerce',
|
||||
icon: 'shopping-cart-outline',
|
||||
title: 'Dashboard',
|
||||
icon: 'home-outline',
|
||||
link: '/pages/dashboard',
|
||||
home: true,
|
||||
},
|
||||
{
|
||||
title: 'IoT Dashboard',
|
||||
icon: 'home-outline',
|
||||
link: '/pages/iot-dashboard',
|
||||
},
|
||||
{
|
||||
title: 'FEATURES',
|
||||
group: true,
|
||||
},
|
||||
{
|
||||
title: 'Layout',
|
||||
icon: 'layout-outline',
|
||||
title: 'Identity Verification',
|
||||
icon: 'person-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Stepper',
|
||||
link: '/pages/layout/stepper',
|
||||
title: 'Manual Lookup',
|
||||
link: '/pages/identity/manual-lookup',
|
||||
},
|
||||
{
|
||||
title: 'List',
|
||||
link: '/pages/layout/list',
|
||||
},
|
||||
{
|
||||
title: 'Infinite List',
|
||||
link: '/pages/layout/infinite-list',
|
||||
},
|
||||
{
|
||||
title: 'Accordion',
|
||||
link: '/pages/layout/accordion',
|
||||
},
|
||||
{
|
||||
title: 'Tabs',
|
||||
pathMatch: 'prefix',
|
||||
link: '/pages/layout/tabs',
|
||||
title: 'Results History',
|
||||
link: '/pages/identity/results-history',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Forms',
|
||||
icon: 'edit-2-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Form Inputs',
|
||||
link: '/pages/forms/inputs',
|
||||
},
|
||||
{
|
||||
title: 'Form Layouts',
|
||||
link: '/pages/forms/layouts',
|
||||
},
|
||||
{
|
||||
title: 'Buttons',
|
||||
link: '/pages/forms/buttons',
|
||||
},
|
||||
{
|
||||
title: 'Datepicker',
|
||||
link: '/pages/forms/datepicker',
|
||||
},
|
||||
],
|
||||
title: 'Countries',
|
||||
icon: 'globe-outline',
|
||||
link: '/pages/countries',
|
||||
},
|
||||
{
|
||||
title: 'UI Features',
|
||||
icon: 'keypad-outline',
|
||||
link: '/pages/ui-features',
|
||||
title: 'Account',
|
||||
icon: 'settings-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Grid',
|
||||
link: '/pages/ui-features/grid',
|
||||
title: 'Profile',
|
||||
link: '/pages/account/profile',
|
||||
},
|
||||
{
|
||||
title: 'Icons',
|
||||
link: '/pages/ui-features/icons',
|
||||
},
|
||||
{
|
||||
title: 'Typography',
|
||||
link: '/pages/ui-features/typography',
|
||||
},
|
||||
{
|
||||
title: 'Animated Searches',
|
||||
link: '/pages/ui-features/search-fields',
|
||||
title: 'Settings',
|
||||
link: '/pages/account/settings',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Modal & Overlays',
|
||||
icon: 'browser-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Dialog',
|
||||
link: '/pages/modal-overlays/dialog',
|
||||
},
|
||||
{
|
||||
title: 'Window',
|
||||
link: '/pages/modal-overlays/window',
|
||||
},
|
||||
{
|
||||
title: 'Popover',
|
||||
link: '/pages/modal-overlays/popover',
|
||||
},
|
||||
{
|
||||
title: 'Toastr',
|
||||
link: '/pages/modal-overlays/toastr',
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
link: '/pages/modal-overlays/tooltip',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Extra Components',
|
||||
icon: 'message-circle-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Calendar',
|
||||
link: '/pages/extra-components/calendar',
|
||||
},
|
||||
{
|
||||
title: 'Progress Bar',
|
||||
link: '/pages/extra-components/progress-bar',
|
||||
},
|
||||
{
|
||||
title: 'Spinner',
|
||||
link: '/pages/extra-components/spinner',
|
||||
},
|
||||
{
|
||||
title: 'Alert',
|
||||
link: '/pages/extra-components/alert',
|
||||
},
|
||||
{
|
||||
title: 'Calendar Kit',
|
||||
link: '/pages/extra-components/calendar-kit',
|
||||
},
|
||||
{
|
||||
title: 'Chat',
|
||||
link: '/pages/extra-components/chat',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Maps',
|
||||
icon: 'map-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Google Maps',
|
||||
link: '/pages/maps/gmaps',
|
||||
},
|
||||
{
|
||||
title: 'Leaflet Maps',
|
||||
link: '/pages/maps/leaflet',
|
||||
},
|
||||
{
|
||||
title: 'Bubble Maps',
|
||||
link: '/pages/maps/bubble',
|
||||
},
|
||||
{
|
||||
title: 'Search Maps',
|
||||
link: '/pages/maps/searchmap',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Charts',
|
||||
icon: 'pie-chart-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Echarts',
|
||||
link: '/pages/charts/echarts',
|
||||
},
|
||||
{
|
||||
title: 'Charts.js',
|
||||
link: '/pages/charts/chartjs',
|
||||
},
|
||||
{
|
||||
title: 'D3',
|
||||
link: '/pages/charts/d3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Editors',
|
||||
icon: 'text-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'TinyMCE',
|
||||
link: '/pages/editors/tinymce',
|
||||
},
|
||||
{
|
||||
title: 'CKEditor',
|
||||
link: '/pages/editors/ckeditor',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Tables & Data',
|
||||
icon: 'grid-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Smart Table',
|
||||
link: '/pages/tables/smart-table',
|
||||
},
|
||||
{
|
||||
title: 'Tree Grid',
|
||||
link: '/pages/tables/tree-grid',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Miscellaneous',
|
||||
icon: 'shuffle-2-outline',
|
||||
children: [
|
||||
{
|
||||
title: '404',
|
||||
link: '/pages/miscellaneous/404',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Auth',
|
||||
icon: 'lock-outline',
|
||||
children: [
|
||||
{
|
||||
title: 'Login',
|
||||
link: '/auth/login',
|
||||
},
|
||||
{
|
||||
title: 'Register',
|
||||
link: '/auth/register',
|
||||
},
|
||||
{
|
||||
title: 'Request Password',
|
||||
link: '/auth/request-password',
|
||||
},
|
||||
{
|
||||
title: 'Reset Password',
|
||||
link: '/auth/reset-password',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
];
|
||||
|
|
@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
|
|||
|
||||
import { PagesComponent } from './pages.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { ECommerceComponent } from './e-commerce/e-commerce.component';
|
||||
import { NotFoundComponent } from './miscellaneous/not-found/not-found.component';
|
||||
|
||||
const routes: Routes = [{
|
||||
|
|
@ -12,61 +11,22 @@ const routes: Routes = [{
|
|||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: ECommerceComponent,
|
||||
},
|
||||
{
|
||||
path: 'iot-dashboard',
|
||||
component: DashboardComponent,
|
||||
},
|
||||
{
|
||||
path: 'layout',
|
||||
loadChildren: () => import('./layout/layout.module')
|
||||
.then(m => m.LayoutModule),
|
||||
path: 'identity',
|
||||
loadChildren: () => import('./identity/identity.module')
|
||||
.then(m => m.IdentityModule),
|
||||
},
|
||||
{
|
||||
path: 'forms',
|
||||
loadChildren: () => import('./forms/forms.module')
|
||||
.then(m => m.FormsModule),
|
||||
path: 'countries',
|
||||
loadChildren: () => import('./countries/countries.module')
|
||||
.then(m => m.CountriesModule),
|
||||
},
|
||||
{
|
||||
path: 'ui-features',
|
||||
loadChildren: () => import('./ui-features/ui-features.module')
|
||||
.then(m => m.UiFeaturesModule),
|
||||
},
|
||||
{
|
||||
path: 'modal-overlays',
|
||||
loadChildren: () => import('./modal-overlays/modal-overlays.module')
|
||||
.then(m => m.ModalOverlaysModule),
|
||||
},
|
||||
{
|
||||
path: 'extra-components',
|
||||
loadChildren: () => import('./extra-components/extra-components.module')
|
||||
.then(m => m.ExtraComponentsModule),
|
||||
},
|
||||
{
|
||||
path: 'maps',
|
||||
loadChildren: () => import('./maps/maps.module')
|
||||
.then(m => m.MapsModule),
|
||||
},
|
||||
{
|
||||
path: 'charts',
|
||||
loadChildren: () => import('./charts/charts.module')
|
||||
.then(m => m.ChartsModule),
|
||||
},
|
||||
{
|
||||
path: 'editors',
|
||||
loadChildren: () => import('./editors/editors.module')
|
||||
.then(m => m.EditorsModule),
|
||||
},
|
||||
{
|
||||
path: 'tables',
|
||||
loadChildren: () => import('./tables/tables.module')
|
||||
.then(m => m.TablesModule),
|
||||
},
|
||||
{
|
||||
path: 'miscellaneous',
|
||||
loadChildren: () => import('./miscellaneous/miscellaneous.module')
|
||||
.then(m => m.MiscellaneousModule),
|
||||
path: 'account',
|
||||
loadChildren: () => import('./account/account.module')
|
||||
.then(m => m.AccountModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
|
|
@ -85,4 +45,4 @@ const routes: Routes = [{
|
|||
exports: [RouterModule],
|
||||
})
|
||||
export class PagesRoutingModule {
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,8 @@ import { NbMenuModule } from '@nebular/theme';
|
|||
import { ThemeModule } from '../@theme/theme.module';
|
||||
import { PagesComponent } from './pages.component';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
import { ECommerceModule } from './e-commerce/e-commerce.module';
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
import { MiscellaneousModule } from './miscellaneous/miscellaneous.module';
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
|
@ -14,7 +13,6 @@ import { MiscellaneousModule } from './miscellaneous/miscellaneous.module';
|
|||
ThemeModule,
|
||||
NbMenuModule,
|
||||
DashboardModule,
|
||||
ECommerceModule,
|
||||
MiscellaneousModule,
|
||||
],
|
||||
declarations: [
|
||||
|
|
@ -22,4 +20,4 @@ import { MiscellaneousModule } from './miscellaneous/miscellaneous.module';
|
|||
],
|
||||
})
|
||||
export class PagesModule {
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,15 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ngx-admin Demo Application</title>
|
||||
<title>IdentityPulse - Identity Verification Platform</title>
|
||||
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<script defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCpVhQiwAllg1RAFaxMWSpQruuGARy0Y1k&libraries=places"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ngx-app>Loading...</ngx-app>
|
||||
|
|
@ -25,4 +26,4 @@
|
|||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue