diff --git a/apps/gauzy/src/app/pages/dashboard/dashboard.component.ts b/apps/gauzy/src/app/pages/dashboard/dashboard.component.ts index 9f5023499ae..bad0c8bb140 100644 --- a/apps/gauzy/src/app/pages/dashboard/dashboard.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/dashboard.component.ts @@ -72,11 +72,11 @@ export class DashboardComponent extends TranslationBaseComponent implements Afte if (this._store.hasAnyPermission(PermissionsEnum.ADMIN_DASHBOARD_VIEW, PermissionsEnum.TEAM_DASHBOARD)) { // Register the teams tab this._pageTabRegistryService.registerPageTab({ - tabsetId: 'dashboard', // The identifier for the tabset + tabsetId: this.tabsetId, // The identifier for the tabset tabId: 'teams', // The identifier for the tab tabsetType: 'route', // The type of tabset to use route: this.getRoute('teams'), // The route for the tab - tabTitle: () => this.getTranslation('ORGANIZATIONS_PAGE.TEAMS'), // The title for the tab + tabTitle: (_i18n) => _i18n.getTranslation('ORGANIZATIONS_PAGE.TEAMS'), // The title for the tab tabIcon: 'people-outline', // The icon for the tab responsive: true, // Whether the tab is responsive activeLinkOptions: { exact: false }, // The options for the active link @@ -93,11 +93,11 @@ export class DashboardComponent extends TranslationBaseComponent implements Afte ) { // Register the project management tab this._pageTabRegistryService.registerPageTab({ - tabsetId: 'dashboard', // The identifier for the tabset + tabsetId: this.tabsetId, // The identifier for the tabset tabId: 'project-management', // The identifier for the tab tabsetType: 'route', // The type of tabset to use route: this.getRoute('project-management'), // The route for the tab - tabTitle: () => this.getTranslation('DASHBOARD_PAGE.PROJECT_MANAGEMENT'), // The title for the tab + tabTitle: (_i18n) => _i18n.getTranslation('DASHBOARD_PAGE.PROJECT_MANAGEMENT'), // The title for the tab tabIcon: 'browser-outline', // The icon for the tab responsive: true, // Whether the tab is responsive activeLinkOptions: { exact: false }, // The options for the active link @@ -111,11 +111,11 @@ export class DashboardComponent extends TranslationBaseComponent implements Afte ) { // Register the time tracking tab this._pageTabRegistryService.registerPageTab({ - tabsetId: 'dashboard', // The identifier for the tabset + tabsetId: this.tabsetId, // The identifier for the tabset tabId: 'time-tracking', // The identifier for the tab tabsetType: 'route', // The type of tabset to use route: this.getRoute('time-tracking'), // The route for the tab - tabTitle: () => this.getTranslation('TIMESHEET.TIME_TRACKING'), // The title for the tab + tabTitle: (_i18n) => _i18n.getTranslation('TIMESHEET.TIME_TRACKING'), // The title for the tab tabIcon: 'clock-outline', // The icon for the tab responsive: true, // Whether the tab is responsive activeLinkOptions: { exact: false }, // The options for the active link @@ -138,11 +138,11 @@ export class DashboardComponent extends TranslationBaseComponent implements Afte if (!this.selectedEmployee || !this.selectedEmployee.id) { // Register the accounting tab this._pageTabRegistryService.registerPageTab({ - tabsetId: 'dashboard', // The identifier for the tabset + tabsetId: this.tabsetId, // The identifier for the tabset tabId: 'accounting', // The identifier for the tab tabsetType: 'route', // The type of tabset to use route: this.getRoute('accounting'), // The route for the tab - tabTitle: () => this.getTranslation('DASHBOARD_PAGE.ACCOUNTING'), // The title for the tab + tabTitle: (_i18n) => _i18n.getTranslation('DASHBOARD_PAGE.ACCOUNTING'), // The title for the tab tabIcon: 'credit-card-outline', // The icon for the tab responsive: true, // Whether the tab is responsive activeLinkOptions: { exact: false }, // The options for the active link @@ -161,11 +161,11 @@ export class DashboardComponent extends TranslationBaseComponent implements Afte if (this.selectedEmployee && this.selectedEmployee.id) { // Register the human resources tab this._pageTabRegistryService.registerPageTab({ - tabsetId: 'dashboard', // The identifier for the tabset + tabsetId: this.tabsetId, // The identifier for the tabset tabId: 'hr', // The identifier for the tab tabsetType: 'route', // The type of tabset to use route: this.getRoute('hr'), // The route for the tab - tabTitle: () => this.getTranslation('DASHBOARD_PAGE.HUMAN_RESOURCES'), // The title for the tab + tabTitle: (_i18n) => _i18n.getTranslation('DASHBOARD_PAGE.HUMAN_RESOURCES'), // The title for the tab tabIcon: 'person-outline', // The icon for the tab responsive: true, // Whether the tab is responsive activeLinkOptions: { exact: false }, // The options for the active link diff --git a/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.html b/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.html index 3ba920d4393..5578d94fe8c 100644 --- a/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.html +++ b/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.html @@ -15,24 +15,18 @@

[buttonTemplate]="actionButtons" > - - - - - - - - - - - - - + + + + + +
+
+
+ +
- + +
+ + +
+
+ +
{{ 'COMING_SOON' | translate }}
+
+
+
+
@@ -107,12 +115,3 @@

- - -
-
- -
{{ 'COMING_SOON' | translate }}
-
-
-
diff --git a/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.ts b/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.ts index a36bbbe3509..a3a971dbbb6 100644 --- a/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.ts +++ b/packages/plugins/job-employee-ui/src/lib/components/job-employee/job-employee.component.ts @@ -1,7 +1,7 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { CurrencyPipe } from '@angular/common'; import { HttpClient } from '@angular/common/http'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, merge, Subject } from 'rxjs'; import { debounceTime, filter, tap } from 'rxjs/operators'; import { NbTabComponent } from '@nebular/theme'; @@ -17,7 +17,9 @@ import { JobService, ServerDataSource, Store, - ToastrService + ToastrService, + PageTabsetRegistryId, + PageTabRegistryService } from '@gauzy/ui-core/core'; import { I18nService } from '@gauzy/ui-core/i18n'; import { @@ -45,6 +47,7 @@ export enum JobSearchTabsEnum { providers: [CurrencyPipe] }) export class JobEmployeeComponent extends PaginationFilterBaseComponent implements AfterViewInit, OnInit, OnDestroy { + public tabsetId: PageTabsetRegistryId = this._route.snapshot.data.tabsetId; // The identifier for the tabset public jobSearchTabsEnum = JobSearchTabsEnum; public loading: boolean = false; public settingsSmartTable: any; @@ -56,33 +59,34 @@ export class JobEmployeeComponent extends PaginationFilterBaseComponent implemen public selectedEmployee: IEmployee; public disableButton: boolean = true; + // Template References + @ViewChild('tableLayout', { static: true }) tableLayout: TemplateRef; // Template reference for the table layout tab + @ViewChild('comingSoon', { static: true }) comingSoon: TemplateRef; // Template reference for the coming soon tab + constructor( translateService: TranslateService, private readonly _http: HttpClient, + private readonly _route: ActivatedRoute, private readonly _router: Router, + private readonly _ngxPermissionsService: NgxPermissionsService, private readonly _store: Store, private readonly _employeesService: EmployeesService, private readonly _jobService: JobService, private readonly _toastrService: ToastrService, private readonly _currencyPipe: CurrencyPipe, - private readonly _ngxPermissionsService: NgxPermissionsService, private readonly _i18nService: I18nService, - readonly _pageDataTableRegistryService: PageDataTableRegistryService + readonly _pageDataTableRegistryService: PageDataTableRegistryService, + readonly _pageTabRegistryService: PageTabRegistryService ) { super(translateService); - - // Register data table columns - this.registerDataTableColumns(_pageDataTableRegistryService); } ngOnInit(): void { - this._applyTranslationOnSmartTable(); - this._loadSmartTableSettings(); - - // Initialize UI permissions - this.initializeUiPermissions(); - // Initialize UI languages and Update Locale - this.initializeUiLanguagesAndLocale(); + this._applyTranslationOnSmartTable(); // + this._loadSmartTableSettings(); // Load smart table settings + this.initializeUiPermissions(); // Initialize UI permissions + this.initializeUiLanguagesAndLocale(); // Initialize UI languages and Update Locale + this._initializePageElements(); // Register page elements } ngAfterViewInit(): void { @@ -135,6 +139,59 @@ export class JobEmployeeComponent extends PaginationFilterBaseComponent implemen .subscribe(); } + /** + * Initializes page elements by registering page tabs and data table columns. + * + * This method centralizes the logic for setting up page-related configurations, + * ensuring that the necessary page tabs and data table columns are registered + * upon initialization. + */ + private _initializePageElements(): void { + // Register page elements + this.registerPageTabs(this._pageTabRegistryService); // Register page tabs + this.registerDataTableColumns(this._pageDataTableRegistryService); // Register data table columns + } + + /** + * Register page tabs for the JobEmployee + * + * @param _pageTabRegistryService + */ + registerPageTabs(_pageTabRegistryService: PageTabRegistryService): void { + // Register the browse tab + _pageTabRegistryService.registerPageTab({ + tabsetId: this.tabsetId, // The identifier for the tabset + tabId: 'browse', // The identifier for the tab + tabsetType: 'standard', // The type of tabset to use + tabTitle: (_i18n) => _i18n.getTranslation('JOB_EMPLOYEE.BROWSE'), // The title for the tab + order: 1, // The order of the tab, + responsive: true, // Whether the tab is responsive, + template: this.tableLayout // The template to be rendered in the tab + }); + + // Register the search tab + _pageTabRegistryService.registerPageTab({ + tabsetId: this.tabsetId, // The identifier for the tabset + tabId: 'search', // The identifier for the tab + tabsetType: 'standard', // The type of tabset to use + tabTitle: (_i18n) => _i18n.getTranslation('JOB_EMPLOYEE.SEARCH'), // The title for the tab + order: 2, // The order of the tab, + responsive: true, // Whether the tab is responsive, + template: this.comingSoon // The template to be rendered in the tab + }); + + // Register the history tab + _pageTabRegistryService.registerPageTab({ + tabsetId: this.tabsetId, // The identifier for the tabset + tabId: 'history', // The identifier for the tab + tabsetType: 'standard', // The type of tabset to use + tabTitle: (_i18n) => _i18n.getTranslation('JOB_EMPLOYEE.HISTORY'), // The title for the tab + order: 3, // The order of the tab, + responsive: true, // Whether the tab is responsive, + template: this.comingSoon // The template to be rendered in the tab + }); + } + /** * Register data table columns for the JobEmployee * diff --git a/packages/plugins/job-employee-ui/src/lib/job-employee.module.ts b/packages/plugins/job-employee-ui/src/lib/job-employee.module.ts index 2e329b1c92b..12a9888d57c 100644 --- a/packages/plugins/job-employee-ui/src/lib/job-employee.module.ts +++ b/packages/plugins/job-employee-ui/src/lib/job-employee.module.ts @@ -14,7 +14,7 @@ import { NgxPermissionsModule } from 'ngx-permissions'; import { LanguagesEnum } from '@gauzy/contracts'; import { PageRouteRegistryService } from '@gauzy/ui-core/core'; import { HttpLoaderFactory } from '@gauzy/ui-core/i18n'; -import { SharedModule, SmartDataViewLayoutModule } from '@gauzy/ui-core/shared'; +import { DynamicTabsModule, SharedModule, SmartDataViewLayoutModule } from '@gauzy/ui-core/shared'; import { createJobEmployeeRoutes } from './job-employee.routes'; import { JobEmployeeComponent } from './components/job-employee/job-employee.component'; @@ -45,7 +45,8 @@ const THIRD_PARTY_MODULES = [ ...NB_MODULES, ...THIRD_PARTY_MODULES, SharedModule, - SmartDataViewLayoutModule + SmartDataViewLayoutModule, + DynamicTabsModule ], providers: [ { diff --git a/packages/plugins/job-employee-ui/src/lib/job-employee.routes.ts b/packages/plugins/job-employee-ui/src/lib/job-employee.routes.ts index 71de96fd9ed..06c0b0d69d0 100644 --- a/packages/plugins/job-employee-ui/src/lib/job-employee.routes.ts +++ b/packages/plugins/job-employee-ui/src/lib/job-employee.routes.ts @@ -15,6 +15,9 @@ export const createJobEmployeeRoutes = (_pageRouteRegistryService: PageRouteRegi component: JobEmployeeComponent, canActivate: [PermissionsGuard], data: { + // The tabset identifier for the route + tabsetId: 'job-employee', + // The permission required to access the route permissions: { only: [PermissionsEnum.ORG_JOB_EMPLOYEE_VIEW], redirectTo: '/pages/jobs/search' diff --git a/packages/ui-core/core/src/lib/services/page/page-tab-registry.service.ts b/packages/ui-core/core/src/lib/services/page/page-tab-registry.service.ts index 2d818d7bf3f..d8bd4aa4ac4 100644 --- a/packages/ui-core/core/src/lib/services/page/page-tab-registry.service.ts +++ b/packages/ui-core/core/src/lib/services/page/page-tab-registry.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Type } from '@angular/core'; +import { Injectable, TemplateRef, Type } from '@angular/core'; import { PageTabsetRegistryId } from '../../common/component-registry.types'; import { IPageTabRegistry, PageTabRegistryConfig } from './page-tab-registry.types'; @@ -66,6 +66,7 @@ export class PageTabRegistryService implements IPageTabRegistry { config.order = config.order ?? 0; // Set the default order to 0 if not provided config.hide = config.hide ?? false; // Set the default hide to false if not provided config.responsive = config.responsive ?? true; // Set the default responsive to true if not provided + config.active = config.active ?? false; // Set the default active to false if not provided // Find the index of an existing tab with the same tabId in the specified tab set const existing = tabs.findIndex((tab) => tab.tabId === config.tabId); @@ -199,17 +200,20 @@ export class PageTabRegistryService implements IPageTabRegistry { /** * @description - * Retrieves the component associated with a specific tab ID for a given tabset. + * Retrieves the component or template associated with a specific tab ID for a given tabset. * * This method looks up the tabset using the provided `tabsetId`, then searches for the tab - * with the specified `tabId` within that tabset. If the tab is found, it returns the associated - * component; otherwise, it returns `undefined`. + * with the specified `tabId` within that tabset. If the tab is found, it returns either the + * component or the template based on the tab's configuration; otherwise, it returns `undefined`. * * @param tabsetId The identifier of the tabset to retrieve tabs from. - * @param tabId The identifier of the tab whose component is to be retrieved. - * @returns The component associated with the specified tab ID, or `undefined` if the tab or component is not found. + * @param tabId The identifier of the tab whose component or template is to be retrieved. + * @returns The component or template associated with the specified tab ID, or `undefined` if neither is found. */ - public getComponentForTab(tabsetId: PageTabsetRegistryId, tabId: string): Type | undefined { + public getComponentOrTemplateForTab( + tabsetId: PageTabsetRegistryId, + tabId: string + ): Type | TemplateRef | undefined { // Retrieve the list of tabs for the specified tabsetId const tabs = this.getPageTabset(tabsetId); @@ -218,8 +222,8 @@ export class PageTabRegistryService implements IPageTabRegistry { // Find the tab with the specified tabId const tab = tabs.find((t) => t.tabId === tabId); - // Return the component associated with the tab ID or undefined if not found - return tab?.component; + // Return the component if it exists, otherwise return the template + return tab?.component || tab?.template; } // Return undefined if the tabs could not be retrieved or are not an array diff --git a/packages/ui-core/core/src/lib/services/page/page-tab-registry.types.ts b/packages/ui-core/core/src/lib/services/page/page-tab-registry.types.ts index a06a6ecc9ab..3bc98624a67 100644 --- a/packages/ui-core/core/src/lib/services/page/page-tab-registry.types.ts +++ b/packages/ui-core/core/src/lib/services/page/page-tab-registry.types.ts @@ -1,11 +1,36 @@ -import { Type } from '@angular/core'; +import { TemplateRef, Type } from '@angular/core'; import { NbRouteTab } from '@nebular/theme'; +import { I18nService } from '@gauzy/ui-core/i18n'; import { PageTabsetRegistryId } from '../../common/component-registry.types'; +/** + * Custom page tab configuration options. + */ +export interface CustomNbRouteTab extends NbRouteTab { + /** + * @description + * Specifies if the tab is active. + */ + active?: boolean; + + /** + * @description + * The component to be loaded in the tab. This can be a component reference or a string identifier + * for lazy loading the component. + */ + component?: Type; + + /** + * @description + * The template to be rendered in the tab. This is an alternative to the component. + */ + template?: TemplateRef; +} + /** * Page tab configuration options. */ -export interface PageTabRegistryConfig extends NbRouteTab { +export interface PageTabRegistryConfig extends CustomNbRouteTab { /** * @description * The tabset identifier for the page tabset. @@ -22,7 +47,7 @@ export interface PageTabRegistryConfig extends NbRouteTab { * @description * The translatable key for the tab title. */ - tabTitle: string | (() => string); + tabTitle: string | ((_: I18nService) => string); /** * @description @@ -30,13 +55,6 @@ export interface PageTabRegistryConfig extends NbRouteTab { */ tabIcon?: string; - /** - * @description - * The component to be loaded in the tab. This can be a component reference or a string identifier - * for lazy loading the component. - */ - component?: Type; - /** * @description * The order of the tab in the tabs bar. @@ -50,12 +68,6 @@ export interface PageTabRegistryConfig extends NbRouteTab { */ tabsetType: 'route' | 'standard'; - /** - * @description - * Specifies if the tab is active. - */ - active?: boolean; - /** * @description * Specifies if the tab is hidden for any reason. diff --git a/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.html b/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.html index 8e6cc5647dd..aade4e115a2 100644 --- a/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.html +++ b/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.html @@ -5,14 +5,26 @@ - {{ tabs | json }} - + [active]="tab.active" + [disabled]="tab.disabled" + > + + + + + + + + + + + + diff --git a/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.ts b/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.ts index 33106d93839..5a6c6f233ab 100644 --- a/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.ts +++ b/packages/ui-core/shared/src/lib/components/dynamic-tabs/dynamic-tabs.component.ts @@ -1,10 +1,25 @@ -import { Component, OnInit, Input, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { + Component, + OnInit, + Input, + OnDestroy, + ChangeDetectorRef, + ViewContainerRef, + ComponentFactoryResolver, + Type +} from '@angular/core'; import { NbRouteTab } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { tap } from 'rxjs/operators'; import { Subject } from 'rxjs/internal/Subject'; -import { PageTabRegistryConfig, PageTabRegistryService, PageTabsetRegistryId } from '@gauzy/ui-core/core'; +import { + CustomNbRouteTab, + PageTabRegistryConfig, + PageTabRegistryService, + PageTabsetRegistryId +} from '@gauzy/ui-core/core'; +import { I18nService } from '@gauzy/ui-core/i18n'; @UntilDestroy() @Component({ @@ -38,8 +53,10 @@ export class DynamicTabsComponent implements OnInit, OnDestroy { constructor( private readonly _cdr: ChangeDetectorRef, + private readonly _componentFactoryResolver: ComponentFactoryResolver, + private readonly _translateService: TranslateService, private readonly _pageTabRegistryService: PageTabRegistryService, - private readonly _translateService: TranslateService + readonly _i18n: I18nService ) {} ngOnInit(): void { @@ -96,21 +113,34 @@ export class DynamicTabsComponent implements OnInit, OnDestroy { * @param tabsetId The identifier for the tabset. * @returns An array of NbRouteTab objects representing the registered tabs. */ - getRegisteredNbTabs(tabsetId: PageTabsetRegistryId): NbRouteTab[] { + getRegisteredNbTabs(tabsetId: PageTabsetRegistryId): CustomNbRouteTab[] { // Map each tab configuration to an NbRouteTab object return this.getRegisteredTabs(tabsetId).map((tab: PageTabRegistryConfig): NbRouteTab => { - // Create a new route object - const route: NbRouteTab = { + // Create a new route tab object + const route: CustomNbRouteTab = { ...(tab.tabTitle && { - title: typeof tab.tabTitle === 'function' ? tab.tabTitle() : tab.tabTitle + title: typeof tab.tabTitle === 'function' ? tab.tabTitle(this._i18n) : tab.tabTitle }), ...(tab.route && { route: tab.route }), ...(tab.tabIcon && { icon: tab.tabIcon }), ...(tab.responsive && { responsive: tab.responsive }), ...(tab.activeLinkOptions && { activeLinkOptions: tab.activeLinkOptions }), - disabled: !!tab.disabled + disabled: !!tab.disabled, + active: !!tab.active }; + // Check if the tabset is a router tabset + if (!this.isRouterTabset) { + // Check if the route configuration has a component or loadChildren property + if (tab.template) { + // Set the template property to the config object + route.template = tab.template; + } else if (tab.component) { + // Set the component property to the config object + route.component = tab.component; + } + } + // Return the route object return route; }); @@ -128,7 +158,7 @@ export class DynamicTabsComponent implements OnInit, OnDestroy { this._translateService.onLangChange .pipe( // Re-initialize the tabs when the language changes - tap(() => this._initializeTabs()), + tap(() => this.reload$.next(true)), // Automatically unsubscribe when the component is destroyed untilDestroyed(this) ) @@ -136,17 +166,20 @@ export class DynamicTabsComponent implements OnInit, OnDestroy { } /** - * Create dynamic components for the tabs + * Create and insert a dynamic component into the specified ViewContainerRef. + * + * @param component The component to be created dynamically. + * @param viewContainer The ViewContainerRef where the component should be inserted. */ - createDynamicComponents(): void { - // Add logic to create dynamic components for the tabset - } + createDynamicComponentForTab(component: Type, viewContainer: ViewContainerRef): void { + // Resolve the component factory for the given component type + const factory = this._componentFactoryResolver.resolveComponentFactory(component); - /** - * Create static tabs for the tabset - */ - createStaticTabs(): void { - // Add logic to create static tabs for NbTabsetComponent if necessary + // Clear any existing view in the container + viewContainer.clear(); + + // Create and insert the component into the view container + viewContainer.createComponent(factory); } ngOnDestroy(): void {}