From 95fc5bf17f86b0c66635da5fe8770a26d183e0f5 Mon Sep 17 00:00:00 2001
From: Gagan7900 <54118395+Gagan7900@users.noreply.github.com>
Date: Sun, 26 Jul 2026 14:11:19 +0530
Subject: [PATCH] Organization Onboarding Workflow
---
.../core/interceptors/loading-interceptor.ts | 2 +-
src/app/core/services/common/menu.data.ts | 84 ++--
src/app/features/billing/billing.routes.ts | 9 -
.../pages/billing-list/billing-list.html | 5 -
.../pages/billing-list/billing-list.ts | 11 -
.../cities/pages/city-list/city-list.html | 14 +-
.../cities/pages/city-list/city-list.ts | 3 +-
.../states/pages/state-list/state-list.html | 14 +-
.../states/pages/state-list/state-list.ts | 3 +-
.../localization/localization.routes.ts | 9 -
.../localization-list/localization-list.html | 4 -
.../localization-list/localization-list.ts | 11 -
.../features/monitoring/monitoring.routes.ts | 9 -
.../monitoring-dashboard.html | 4 -
.../monitoring-dashboard.scss | 0
.../monitoring-dashboard.ts | 11 -
.../organizations/dashboard/dashboard.html | 63 +++
.../dashboard/dashboard.scss} | 0
.../organizations/dashboard/dashboard.ts | 263 ++++++++++
.../organization-list/organization-list.html | 45 +-
.../organization-list/organization-list.scss | 1 +
.../organization-list/organization-list.ts | 246 +++++++++-
.../onboarding-stepper.html | 456 +++++++++---------
.../onboarding-stepper.scss | 129 +++++
.../onboarding-stepper/onboarding-stepper.ts | 30 +-
.../organization-onboarding.html | 117 +++--
.../organization-onboarding.ts | 243 ++++++++--
.../organization-onboarding-state.service.ts | 33 +-
.../organization-onboarding.service.ts | 99 ++--
.../organization-admin.html | 156 ++++++
.../organization-admin/organization-admin.ts | 96 ++++
.../organization-localization.html | 81 +++-
.../organization-localization.ts | 163 ++++---
.../organization-plan-limits.html | 23 +-
.../organization-plan-limits.ts | 19 +-
.../organizations/organizations.routes.ts | 5 +
.../pages/platform-list/platform-list.html | 4 -
.../pages/platform-list/platform-list.scss | 0
.../pages/platform-list/platform-list.ts | 11 -
src/app/features/platform/platform.routes.ts | 9 -
.../settings/pages/branding/branding.html | 121 +++++
.../pages/branding/branding.scss} | 0
.../settings/pages/branding/branding.ts | 26 +
src/app/features/settings/settings.routes.ts | 9 +
src/app/shared/components/button/button.html | 2 +
src/app/shared/components/button/button.scss | 92 ++++
src/app/shared/components/button/button.ts | 3 +
.../components/data-table/data-table.html | 2 +-
.../components/data-table/data-table.ts | 5 +
.../form/autocomplete/autocomplete.scss | 9 +
.../form/autocomplete/autocomplete.ts | 7 +
.../form-date-picker/form-date-picker.html | 42 +-
.../form-date-picker/form-date-picker.scss | 110 +++++
.../components/form/form-field/form-field.ts | 4 +
.../form/form-input/form-input.scss | 4 +-
.../form/form-select/form-select.html | 312 +++++-------
.../form/form-select/form-select.scss | 155 +++++-
.../form/form-select/form-select.ts | 65 ++-
src/app/shared/components/modal/modal.scss | 202 --------
src/app/shell/routes/content.routes.ts | 18 +-
src/styles.scss | 23 +-
61 files changed, 2645 insertions(+), 1051 deletions(-)
delete mode 100644 src/app/features/billing/billing.routes.ts
delete mode 100644 src/app/features/billing/pages/billing-list/billing-list.html
delete mode 100644 src/app/features/billing/pages/billing-list/billing-list.ts
delete mode 100644 src/app/features/localization/localization.routes.ts
delete mode 100644 src/app/features/localization/pages/localization-list/localization-list.html
delete mode 100644 src/app/features/localization/pages/localization-list/localization-list.ts
delete mode 100644 src/app/features/monitoring/monitoring.routes.ts
delete mode 100644 src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.html
delete mode 100644 src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.scss
delete mode 100644 src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.ts
create mode 100644 src/app/features/organizations/dashboard/dashboard.html
rename src/app/features/{billing/pages/billing-list/billing-list.scss => organizations/dashboard/dashboard.scss} (100%)
create mode 100644 src/app/features/organizations/dashboard/dashboard.ts
create mode 100644 src/app/features/organizations/organization-onboarding/steps/organization-admin/organization-admin.html
create mode 100644 src/app/features/organizations/organization-onboarding/steps/organization-admin/organization-admin.ts
delete mode 100644 src/app/features/platform/pages/platform-list/platform-list.html
delete mode 100644 src/app/features/platform/pages/platform-list/platform-list.scss
delete mode 100644 src/app/features/platform/pages/platform-list/platform-list.ts
delete mode 100644 src/app/features/platform/platform.routes.ts
create mode 100644 src/app/features/settings/pages/branding/branding.html
rename src/app/features/{localization/pages/localization-list/localization-list.scss => settings/pages/branding/branding.scss} (100%)
create mode 100644 src/app/features/settings/pages/branding/branding.ts
create mode 100644 src/app/features/settings/settings.routes.ts
diff --git a/src/app/core/interceptors/loading-interceptor.ts b/src/app/core/interceptors/loading-interceptor.ts
index 260cff1f..227e4af4 100644
--- a/src/app/core/interceptors/loading-interceptor.ts
+++ b/src/app/core/interceptors/loading-interceptor.ts
@@ -12,7 +12,7 @@ export const loadingInterceptor: HttpInterceptorFn = (req, next) => {
: req;
if (!shouldSkipLoader) {
- loadingService.show();
+ // loadingService.show();
}
return next(request).pipe(
diff --git a/src/app/core/services/common/menu.data.ts b/src/app/core/services/common/menu.data.ts
index 137a2564..ca6bda4c 100644
--- a/src/app/core/services/common/menu.data.ts
+++ b/src/app/core/services/common/menu.data.ts
@@ -24,67 +24,47 @@ export const SAAS_MENU_DATA: MenuContext = {
selected: false,
dirchange: false,
children: [
- {
- title: 'Global Master',
- type: 'sub',
- active: false,
- selected: false,
- dirchange: false,
- children: [
- { path: '/global-masters/currencies', title: 'Currency', type: 'link', dirchange: false },
- { path: '/global-masters/languages', title: 'Language', type: 'link', dirchange: false },
- { path: '/global-masters/timezones', title: 'Timezone', type: 'link', dirchange: false },
- { path: '/global-masters/countries', title: 'Country', type: 'link', dirchange: false },
- { path: '/global-masters/states', title: 'State', type: 'link', dirchange: false },
- { path: '/global-masters/cities', title: 'City', type: 'link', dirchange: false },
- ],
- },
- {
- title: 'Tenant Master',
- type: 'sub',
- active: false,
- selected: false,
- dirchange: false,
- children: [
- { path: '/tenants', title: 'Tenants', type: 'link', dirchange: false },
- { path: '/tenants/tenant-currencies', title: 'Tenant Currencies', type: 'link', dirchange: false }
- ],
- },
- { path: '/users', title: 'Users', type: 'link', dirchange: false },
- {
- title: 'Configuration',
- type: 'sub',
- active: false,
- selected: false,
- dirchange: false,
- children: [
+ { path: '/global-masters/currencies', title: 'Currency', type: 'link', dirchange: false },
+ { path: '/global-masters/languages', title: 'Language', type: 'link', dirchange: false },
+ { path: '/global-masters/timezones', title: 'Timezone', type: 'link', dirchange: false },
+ { path: '/global-masters/countries', title: 'Country', type: 'link', dirchange: false },
+ { path: '/global-masters/states', title: 'State', type: 'link', dirchange: false },
+ { path: '/global-masters/cities', title: 'City', type: 'link', dirchange: false },
- { path: '/localization', title: 'Localization', type: 'link', dirchange: false },
- {
- title: 'Branding',
- type: 'sub',
- active: false,
- selected: false,
- dirchange: false,
- children: [
- { path: '/theming', title: 'Theming', type: 'link', dirchange: false },
- { path: '/platform', title: 'Platform', type: 'link', dirchange: false },
- ],
- },
- ],
- },
+ // {
+ // title: 'Tenant Master',
+ // type: 'sub',
+ // active: false,
+ // selected: false,
+ // dirchange: false,
+ // children: [
+ // { path: '/tenants', title: 'Tenants', type: 'link', dirchange: false },
+ // { path: '/tenants/tenant-currencies', title: 'Tenant Currencies', type: 'link', dirchange: false }
+ // ],
+ // }
],
},
{
- title: 'Operations',
- icon: '',
+ title: 'Organizations',
+ icon: '',
type: 'sub',
active: false,
selected: false,
dirchange: false,
children: [
- { path: '/billing', title: 'Billing', type: 'link', dirchange: false },
- { path: '/monitoring', title: 'Monitoring', type: 'link', dirchange: false },
+ { path: '/organizations/list', title: 'Organization List', type: 'link', dirchange: false },
+ { path: '/organizations/onboarding', title: 'Organization Onboarding', type: 'link', dirchange: false },
+ ],
+ },
+ {
+ title: 'Settings',
+ icon: '',
+ type: 'sub',
+ active: false,
+ selected: false,
+ dirchange: false,
+ children: [
+ { path: '/settings/branding', title: 'Branding', type: 'link', dirchange: false },
],
},
],
diff --git a/src/app/features/billing/billing.routes.ts b/src/app/features/billing/billing.routes.ts
deleted file mode 100644
index cdd6637a..00000000
--- a/src/app/features/billing/billing.routes.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Routes } from '@angular/router';
-
-export const billingRoutes: Routes = [
- {
- path: '',
- loadComponent: () => import('./pages/billing-list/billing-list').then((m) => m.BillingList),
- data: { childTitle: 'Billing', parentTitle: 'Platform', subParentTitle: 'Subscriptions' },
- },
-];
diff --git a/src/app/features/billing/pages/billing-list/billing-list.html b/src/app/features/billing/pages/billing-list/billing-list.html
deleted file mode 100644
index 0c7f54a7..00000000
--- a/src/app/features/billing/pages/billing-list/billing-list.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
Billing
-
Plans, subscriptions, and tenant subscription upgrades will
- be implemented here.
-
\ No newline at end of file
diff --git a/src/app/features/billing/pages/billing-list/billing-list.ts b/src/app/features/billing/pages/billing-list/billing-list.ts
deleted file mode 100644
index 95265016..00000000
--- a/src/app/features/billing/pages/billing-list/billing-list.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'app-billing-list',
- standalone: true,
- imports: [CommonModule],
- templateUrl: './billing-list.html',
- styleUrl: './billing-list.scss',
-})
-export class BillingList {}
diff --git a/src/app/features/global-masters/cities/pages/city-list/city-list.html b/src/app/features/global-masters/cities/pages/city-list/city-list.html
index 280df3fd..c61cd3bb 100644
--- a/src/app/features/global-masters/cities/pages/city-list/city-list.html
+++ b/src/app/features/global-masters/cities/pages/city-list/city-list.html
@@ -50,10 +50,16 @@
/>
diff --git a/src/app/features/global-masters/cities/pages/city-list/city-list.ts b/src/app/features/global-masters/cities/pages/city-list/city-list.ts
index f535d001..3260e29b 100644
--- a/src/app/features/global-masters/cities/pages/city-list/city-list.ts
+++ b/src/app/features/global-masters/cities/pages/city-list/city-list.ts
@@ -48,6 +48,7 @@ import {
} from '../../../../../shared/components/form/autocomplete/autocomplete.types';
import { Modal } from '../../../../../shared/components/modal/modal';
import { FilterCard } from '../../../../../shared/components/filter-card/filter-card';
+import { Button } from '../../../../../shared/components/button/button';
interface CityTableRow extends DataTableRecord {
id: string;
@@ -66,7 +67,7 @@ interface CityTableRow extends DataTableRecord {
@Component({
selector: 'city-list',
standalone: true,
- imports: [DataTable, Modal, ReactiveFormsModule, FormInput, Autocomplete, FilterCard],
+ imports: [DataTable, Modal, ReactiveFormsModule, FormInput, Autocomplete, FilterCard, Button],
templateUrl: './city-list.html',
styleUrl: './city-list.scss'
})
diff --git a/src/app/features/global-masters/states/pages/state-list/state-list.html b/src/app/features/global-masters/states/pages/state-list/state-list.html
index 0728e910..bd9ea064 100644
--- a/src/app/features/global-masters/states/pages/state-list/state-list.html
+++ b/src/app/features/global-masters/states/pages/state-list/state-list.html
@@ -24,10 +24,16 @@
/>
diff --git a/src/app/features/global-masters/states/pages/state-list/state-list.ts b/src/app/features/global-masters/states/pages/state-list/state-list.ts
index 1c7ed70a..689f16f1 100644
--- a/src/app/features/global-masters/states/pages/state-list/state-list.ts
+++ b/src/app/features/global-masters/states/pages/state-list/state-list.ts
@@ -41,6 +41,7 @@ import { FormInput } from '../../../../../shared/components/form/form-input/form
import { Modal } from '../../../../../shared/components/modal/modal';
import { ConfirmDialog } from '../../../../../shared/components/confirm-dialog/confirm-dialog';
import { FilterCard } from '../../../../../shared/components/filter-card/filter-card';
+import { Button as AppButton } from '../../../../../shared/components/button/button';
interface StateTableRow extends DataTableRecord {
id: string;
@@ -56,7 +57,7 @@ interface StateTableRow extends DataTableRecord {
@Component({
selector: 'state-list',
standalone: true,
- imports: [DataTable, Modal, ReactiveFormsModule, FormInput, Autocomplete, ConfirmDialog, FilterCard],
+ imports: [DataTable, Modal, ReactiveFormsModule, FormInput, Autocomplete, ConfirmDialog, FilterCard, AppButton],
templateUrl: './state-list.html',
styleUrl: './state-list.scss',
})
diff --git a/src/app/features/localization/localization.routes.ts b/src/app/features/localization/localization.routes.ts
deleted file mode 100644
index 48b08968..00000000
--- a/src/app/features/localization/localization.routes.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Routes } from '@angular/router';
-
-export const localizationRoutes: Routes = [
- {
- path: '',
- loadComponent: () => import('./pages/localization-list/localization-list').then((m) => m.LocalizationList),
- data: { childTitle: 'Localization', parentTitle: 'Platform', subParentTitle: 'Translations' },
- },
-];
diff --git a/src/app/features/localization/pages/localization-list/localization-list.html b/src/app/features/localization/pages/localization-list/localization-list.html
deleted file mode 100644
index 0f245ea0..00000000
--- a/src/app/features/localization/pages/localization-list/localization-list.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
Localization
-
Translation browser, editor, import/export, and missing translation reports will be added here.
-
diff --git a/src/app/features/localization/pages/localization-list/localization-list.ts b/src/app/features/localization/pages/localization-list/localization-list.ts
deleted file mode 100644
index 15e5cf8d..00000000
--- a/src/app/features/localization/pages/localization-list/localization-list.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'app-localization-list',
- standalone: true,
- imports: [CommonModule],
- templateUrl: './localization-list.html',
- styleUrl: './localization-list.scss',
-})
-export class LocalizationList {}
diff --git a/src/app/features/monitoring/monitoring.routes.ts b/src/app/features/monitoring/monitoring.routes.ts
deleted file mode 100644
index a5b652a7..00000000
--- a/src/app/features/monitoring/monitoring.routes.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Routes } from '@angular/router';
-
-export const monitoringRoutes: Routes = [
- {
- path: '',
- loadComponent: () => import('./pages/monitoring-dashboard/monitoring-dashboard').then((m) => m.MonitoringDashboard),
- data: { childTitle: 'Monitoring', parentTitle: 'Platform', subParentTitle: 'Observability' },
- },
-];
diff --git a/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.html b/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.html
deleted file mode 100644
index e0d9b527..00000000
--- a/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
Monitoring
-
Tenant counts, subscription status, login audits, and recent failed logins will be added here.
-
diff --git a/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.scss b/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.scss
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.ts b/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.ts
deleted file mode 100644
index e3b78496..00000000
--- a/src/app/features/monitoring/pages/monitoring-dashboard/monitoring-dashboard.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'app-monitoring-dashboard',
- standalone: true,
- imports: [CommonModule],
- templateUrl: './monitoring-dashboard.html',
- styleUrl: './monitoring-dashboard.scss',
-})
-export class MonitoringDashboard {}
diff --git a/src/app/features/organizations/dashboard/dashboard.html b/src/app/features/organizations/dashboard/dashboard.html
new file mode 100644
index 00000000..90954ed7
--- /dev/null
+++ b/src/app/features/organizations/dashboard/dashboard.html
@@ -0,0 +1,63 @@
+
+
+
+
+ @for (card of statCards(); track card.title) {
+
+
+
+
+
+
{{ card.title }}
+
+ {{ card.value }}
+
+
+ {{ card.helper }}
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+
\ No newline at end of file
diff --git a/src/app/features/billing/pages/billing-list/billing-list.scss b/src/app/features/organizations/dashboard/dashboard.scss
similarity index 100%
rename from src/app/features/billing/pages/billing-list/billing-list.scss
rename to src/app/features/organizations/dashboard/dashboard.scss
diff --git a/src/app/features/organizations/dashboard/dashboard.ts b/src/app/features/organizations/dashboard/dashboard.ts
new file mode 100644
index 00000000..abddc57b
--- /dev/null
+++ b/src/app/features/organizations/dashboard/dashboard.ts
@@ -0,0 +1,263 @@
+import { Component, signal, inject } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { DataTable } from '../../../shared/components/data-table/data-table';
+import { DataTableColumn, DataTableRecord } from '../../../shared/components/data-table/data-table.types';
+
+type OrganizationPlan = 'Enterprise' | 'Standard' | 'Trial';
+type OrganizationStatus = 'Active' | 'Trial' | 'Suspended';
+
+interface DashboardStatCard {
+ title: string;
+ value: string;
+ helper: string;
+ accentClass: string;
+ iconClass: string;
+ iconBackgroundClass: string;
+ helperClass: string;
+}
+
+interface RecentOrganizationRow extends DataTableRecord {
+ id: string;
+ code: string;
+ organizationName: string;
+ country: string;
+ plan: OrganizationPlan;
+ status: OrganizationStatus;
+ created: string;
+}
+
+@Component({
+ selector: 'dashboard',
+ standalone: true,
+ imports: [DataTable],
+ templateUrl: './dashboard.html',
+ styleUrl: './dashboard.scss',
+})
+export class Dashboard {
+ private readonly router = inject(Router);
+
+ readonly statCards = signal([
+ {
+ title: 'Total Organizations',
+ value: '24',
+ helper: 'All registered',
+ accentClass: 'border-primary',
+ iconClass: 'ti ti-building-community',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ {
+ title: 'Active',
+ value: '19',
+ helper: 'Currently operational',
+ accentClass: 'border-primary',
+ iconClass: 'ti ti-circle-check',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ {
+ title: 'Trial',
+ value: '3',
+ helper: 'Trial subscriptions',
+ accentClass: 'border-primary',
+ iconClass: 'ri-wallet-2-line',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ {
+ title: 'Suspended',
+ value: '1',
+ helper: 'Temporarily disabled',
+ accentClass: 'border-primary',
+ iconClass: 'ti ti-player-pause',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ {
+ title: 'Expired License',
+ value: '1',
+ helper: 'Needs renewal action',
+ accentClass: 'border-primary',
+ iconClass: 'ti ti-license',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ {
+ title: 'Active Users',
+ value: '482',
+ helper: 'Users with access',
+ accentClass: 'border-primary',
+ iconClass: 'ri-profile-line',
+ iconBackgroundClass: 'bg-primary',
+ helperClass: 'bg-primary/10 text-primary',
+ },
+ ]);
+
+ readonly recentOrganizations = signal([
+ {
+ id: '1',
+ code: 'ORG-0024',
+ organizationName: 'Syscom Group',
+ country: 'Saudi Arabia',
+ plan: 'Enterprise',
+ status: 'Active',
+ created: '09-07-2026',
+ },
+ {
+ id: '2',
+ code: 'ORG-0023',
+ organizationName: 'Acme Trading',
+ country: 'India',
+ plan: 'Standard',
+ status: 'Active',
+ created: '01-07-2026',
+ },
+ {
+ id: '3',
+ code: 'ORG-0022',
+ organizationName: 'Falcon Retail LLC',
+ country: 'UAE',
+ plan: 'Trial',
+ status: 'Trial',
+ created: '28-06-2026',
+ },
+ {
+ id: '4',
+ code: 'ORG-0025',
+ organizationName: 'Syscom Group',
+ country: 'Saudi Arabia',
+ plan: 'Enterprise',
+ status: 'Active',
+ created: '09-07-2026',
+ },
+ {
+ id: '5',
+ code: 'ORG-0026',
+ organizationName: 'Acme Trading',
+ country: 'India',
+ plan: 'Standard',
+ status: 'Active',
+ created: '01-07-2026',
+ },
+ {
+ id: '6',
+ code: 'ORG-0027',
+ organizationName: 'Falcon Retail LLC',
+ country: 'UAE',
+ plan: 'Trial',
+ status: 'Trial',
+ created: '28-06-2026',
+ },
+ {
+ id: '7',
+ code: 'ORG-0028',
+ organizationName: 'Syscom Group',
+ country: 'Saudi Arabia',
+ plan: 'Enterprise',
+ status: 'Active',
+ created: '09-07-2026',
+ },
+ {
+ id: '8',
+ code: 'ORG-0029',
+ organizationName: 'Acme Trading',
+ country: 'India',
+ plan: 'Standard',
+ status: 'Active',
+ created: '01-07-2026',
+ },
+ {
+ id: '9',
+ code: 'ORG-0030',
+ organizationName: 'Falcon Retail LLC',
+ country: 'UAE',
+ plan: 'Trial',
+ status: 'Trial',
+ created: '28-06-2026',
+ },
+ ]);
+
+ readonly columns = signal[]>([
+ { key: 'code', label: 'Code', header: 'Code', sortable: false, align: 'left' },
+ {
+ key: 'organizationName',
+ label: 'Name',
+ header: 'Name',
+ sortable: true,
+ align: 'left',
+ },
+ { key: 'country', label: 'Country', header: 'Country', sortable: false },
+ {
+ key: 'plan',
+ label: 'Plan',
+ header: 'Plan',
+ sortable: true,
+ badge: true,
+ badgeClass: value =>
+ value === 'Trial'
+ ? 'badge bg-warning/10 text-warning'
+ : 'badge bg-light text-defaulttextcolor',
+ },
+ {
+ key: 'status',
+ label: 'Status',
+ header: 'Status',
+ sortable: true,
+ badge: true,
+ badgeClass: value =>
+ value === 'Active'
+ ? 'badge bg-success/10 text-success'
+ : value === 'Trial'
+ ? 'badge bg-warning/10 text-warning'
+ : 'badge bg-danger/10 text-danger',
+ },
+ { key: 'created', label: 'Created', header: 'Created', sortable: false },
+ ]);
+
+ readonly totalRecords = signal(6);
+ readonly queryState = {
+ pageIndex: signal(0)
+ };
+ readonly emptyMessage = signal('No Organizations');
+ readonly emptyDescription = signal('Start by adding your first organization');
+
+ readonly actions = signal([
+ { id: 'edit', label: 'Edit', icon: 'ti ti-edit' },
+ { id: 'delete', label: 'Delete', icon: 'ti ti-trash' },
+ ]);
+
+ onAddOrganization(): void {
+ void this.router.navigate(['/organizations/onboarding']);
+ }
+
+ onSearch(searchTerm: string) {
+ console.log('Search:', searchTerm);
+ }
+
+ onPageChange(event: any) {
+ console.log('Page changed:', event);
+ }
+
+ onSortChange(event: any) {
+ console.log('Sort changed:', event);
+ }
+
+ onActionClick(event: any) {
+ const { actionId, record } = event;
+
+ switch (actionId) {
+ case 'edit':
+ console.log('Edit organization:', record);
+ // Implement edit logic here
+ break;
+ case 'delete':
+ console.log('Delete organization:', record);
+ // Implement delete logic here
+ break;
+ default:
+ console.log('Unknown action:', actionId);
+ }
+ }
+
+}
diff --git a/src/app/features/organizations/organization-list/organization-list.html b/src/app/features/organizations/organization-list/organization-list.html
index 4c6f643d..4e56b450 100644
--- a/src/app/features/organizations/organization-list/organization-list.html
+++ b/src/app/features/organizations/organization-list/organization-list.html
@@ -1 +1,44 @@
-organization-list works!
+
+
+
+
diff --git a/src/app/features/organizations/organization-list/organization-list.scss b/src/app/features/organizations/organization-list/organization-list.scss
index e69de29b..8b137891 100644
--- a/src/app/features/organizations/organization-list/organization-list.scss
+++ b/src/app/features/organizations/organization-list/organization-list.scss
@@ -0,0 +1 @@
+
diff --git a/src/app/features/organizations/organization-list/organization-list.ts b/src/app/features/organizations/organization-list/organization-list.ts
index 0aa9bef6..5becb0d3 100644
--- a/src/app/features/organizations/organization-list/organization-list.ts
+++ b/src/app/features/organizations/organization-list/organization-list.ts
@@ -1,11 +1,251 @@
-import { Component } from '@angular/core';
+import { Component, inject, signal } from '@angular/core';
+import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { ToastrService } from 'ngx-toastr';
+import { of, map } from 'rxjs';
+
+import { DataTable } from '../../../shared/components/data-table/data-table';
+import { DataTableQueryState } from '../../../shared/components/data-table/data-table-query.state';
+import {
+ DataTableAction,
+ DataTableActionEvent,
+ DataTableColumn,
+ DataTablePageEvent,
+ DataTableRecord,
+ DataTableSortEvent,
+} from '../../../shared/components/data-table/data-table.types';
+import { Autocomplete } from '../../../shared/components/form/autocomplete/autocomplete';
+import {
+ AutocompleteDisplayFn,
+ AutocompleteResolveValueFn,
+ AutocompleteSearchFn,
+ AutocompleteValueFn,
+} from '../../../shared/components/form/autocomplete/autocomplete.types';
+import { FilterCard } from '../../../shared/components/filter-card/filter-card';
+import { CountryLookupDto, CountryService } from '../../global-masters/countries/public-api';
+
+type OrganizationStatus = 'Active' | 'Trial' | 'Suspended';
+
+interface CountryLookup {
+ id: string;
+ name: string;
+}
+
+interface OrganizationListRow extends DataTableRecord {
+ id: string;
+ code: string;
+ organizationName: string;
+ countryId: string;
+ country: string;
+ plan: string;
+ status: OrganizationStatus;
+ expiry: string;
+}
@Component({
selector: 'organization-list',
- imports: [],
+ standalone: true,
+ imports: [ReactiveFormsModule, FilterCard, Autocomplete, DataTable],
templateUrl: './organization-list.html',
styleUrl: './organization-list.scss',
})
-export class OrganizationList {
+
+export class OrganizationList {
+ private readonly formBuilder = inject(FormBuilder);
+ private readonly router = inject(Router);
+ private readonly toastr = inject(ToastrService);
+ private readonly countryApi = inject(CountryService);
+
+ readonly queryState = new DataTableQueryState();
+
+ readonly selectedCountryLookup = signal(null);
+ readonly selectedFormCountry = signal(null);
+ readonly selectedCountryId = signal(null);
+ readonly appliedCountryId = signal(null);
+
+ readonly countryFilterForm = this.formBuilder.nonNullable.group({
+ countryId: [''],
+ });
+
+ readonly allOrganizations = signal([
+ {
+ id: '1',
+ code: 'ORG-0024',
+ organizationName: 'Syscom Group',
+ countryId: 'c1',
+ country: 'Saudi Arabia',
+ plan: 'Enterprise',
+ status: 'Active',
+ expiry: '31-12-2026'
+ },
+ {
+ id: '2',
+ code: 'ORG-0023',
+ organizationName: 'Acme Trading',
+ countryId: 'c2',
+ country: 'India',
+ plan: 'Standard',
+ status: 'Active',
+ expiry: '31-03-2027'
+ },
+ {
+ id: '3',
+ code: 'ORG-0022',
+ organizationName: 'Falcon Retail LLC',
+ countryId: 'c3',
+ country: 'UAE',
+ plan: 'Trial',
+ status: 'Trial',
+ expiry: '28-07-2026'
+ },
+ {
+ id: '4',
+ code: 'ORG-0021',
+ organizationName: 'Oasis Foods',
+ countryId: 'c1',
+ country: 'Saudi Arabia',
+ plan: 'Standard',
+ status: 'Suspended',
+ expiry: '15-06-2026'
+ },
+ ]);
+
+ readonly organizations = signal([]);
+ readonly totalRecords = signal(0);
+
+ readonly searchCountries: AutocompleteSearchFn = (term, limit) => this.countryApi.autocomplete(term, limit);
+ readonly displayCountry: AutocompleteDisplayFn = country => country.name;
+ readonly countryValue: AutocompleteValueFn = country => country.id;
+ readonly resolveCountry: AutocompleteResolveValueFn =
+ value => this.countryApi.getCountryById(value).pipe(
+ map(country => ({ id: country.id, iso2: country.iso2, name: country.name }))
+ );
+
+ readonly columns = signal[]>([
+ { key: 'code', label: 'Code', header: 'Code', sortable: true },
+ {
+ key: 'organizationName',
+ label: 'Organization Name',
+ header: 'Organization Name',
+ sortable: true,
+ align: 'left',
+ },
+ { key: 'country', label: 'Country', header: 'Country', sortable: true },
+ {
+ key: 'plan',
+ label: 'Plan',
+ header: 'Plan',
+ sortable: true,
+ badge: true,
+ badgeClass: value =>
+ value === 'Trial'
+ ? 'badge bg-warning/10 text-warning'
+ : 'badge bg-light text-defaulttextcolor',
+ },
+ {
+ key: 'status',
+ label: 'Status',
+ header: 'Status',
+ sortable: true,
+ badge: true,
+ badgeClass: value => {
+ if (value === 'Active') return 'badge bg-success/10 text-success';
+ if (value === 'Trial') return 'badge bg-warning/10 text-warning';
+ return 'badge bg-danger/10 text-danger';
+ },
+ },
+ { key: 'expiry', label: 'Expiry', header: 'Expiry', sortable: true },
+ ]);
+
+ readonly actions = signal[]>([
+ { type: 'view', label: 'View', icon: 'ti ti-eye', className: 'text-info' },
+ { type: 'edit', label: 'Edit', icon: 'ti ti-edit', className: 'text-primary' },
+ {
+ type: 'suspend',
+ label: 'Suspend',
+ icon: 'ti ti-player-pause',
+ className: 'text-warning',
+ visible: row => row.status !== 'Suspended',
+ },
+ ]);
+
+ readonly emptyMessage = signal('No organizations found');
+ readonly emptyDescription = signal('There are no organizations available for the current filters.');
+
+ constructor() {
+ this.refreshTable();
+ }
+
+ onCountryLookupSelected(country: CountryLookupDto): void {
+ this.selectedCountryLookup.set(country);
+ }
+
+ applyCountryFilter(): void {
+ const selectedCountryId = this.countryFilterForm.controls.countryId.value.trim();
+ this.appliedCountryId.set(selectedCountryId || null);
+ this.queryState.pageIndex.set(1);
+ this.refreshTable();
+ }
+
+ onSearch(value: string): void {
+ this.queryState.setSearch(value);
+ this.refreshTable();
+ }
+
+ onPageChange(event: DataTablePageEvent): void {
+ this.queryState.setPage(event);
+ this.refreshTable();
+ }
+
+ onSortChange(event: DataTableSortEvent): void {
+ this.queryState.setSort(event);
+ this.refreshTable();
+ }
+
+ onAddOrganization(): void {
+ void this.router.navigate(['/organizations/onboarding']);
+ }
+
+ onActionClick(event: DataTableActionEvent): void {
+ this.toastr.info(`${event.action.label} clicked for ${event.row.organizationName}`);
+ }
+
+ private refreshTable(): void {
+ const countryId = this.appliedCountryId();
+ const search = this.queryState.searchText().trim().toLowerCase();
+ const sortBy = this.queryState.sortColumn();
+ const sortDir = this.queryState.sortDirection();
+
+ let rows = this.allOrganizations().filter(row => {
+ const matchesCountry = !countryId || row.countryId === countryId;
+ const matchesSearch = !search
+ || row.code.toLowerCase().includes(search)
+ || row.organizationName.toLowerCase().includes(search)
+ || row.country.toLowerCase().includes(search)
+ || row.plan.toLowerCase().includes(search)
+ || row.status.toLowerCase().includes(search)
+ || row.expiry.toLowerCase().includes(search);
+
+ return matchesCountry && matchesSearch;
+ });
+
+ if (sortBy) {
+ rows = [...rows].sort((left, right) => {
+ const leftValue = String(left[sortBy as keyof OrganizationListRow] ?? '').toLowerCase();
+ const rightValue = String(right[sortBy as keyof OrganizationListRow] ?? '').toLowerCase();
+ const compared = leftValue.localeCompare(rightValue);
+ return sortDir === 'asc' ? compared : -compared;
+ });
+ }
+
+ const total = rows.length;
+ const pageIndex = this.queryState.pageIndex();
+ const pageSize = this.queryState.pageSize();
+ const start = (pageIndex - 1) * pageSize;
+ const end = start + pageSize;
+
+ this.totalRecords.set(total);
+ this.organizations.set(rows.slice(start, end));
+ }
}
diff --git a/src/app/features/organizations/organization-onboarding/components/onboarding-stepper/onboarding-stepper.html b/src/app/features/organizations/organization-onboarding/components/onboarding-stepper/onboarding-stepper.html
index 9def9eba..92e57aa2 100644
--- a/src/app/features/organizations/organization-onboarding/components/onboarding-stepper/onboarding-stepper.html
+++ b/src/app/features/organizations/organization-onboarding/components/onboarding-stepper/onboarding-stepper.html
@@ -1,239 +1,255 @@
-
-