orgnanization onboarding stepper form changes, code refactor for custom data-table grid

This commit is contained in:
Gagan7900
2026-07-28 12:07:28 +05:30
parent 95fc5bf17f
commit 9ce058895c
154 changed files with 5480 additions and 9738 deletions
@@ -1,8 +0,0 @@
@if (loadingService.isLoading()) {
<div class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/20">
<button type="button" class="ti-btn ti-btn-disabled ti-btn-primary-full" disabled>
<span class="ti-spinner text-white" role="status" aria-label="loading"></span>
<span>Loading...</span>
</button>
</div>
}
@@ -1,13 +0,0 @@
import { Component,inject } from '@angular/core';
import { LoadingService } from '../../../core/services/loading.service';
@Component({
selector: 'app-loader',
imports: [],
standalone: true,
templateUrl: './app-loader.html',
styleUrl: './app-loader.scss',
})
export class AppLoader {
readonly loadingService = inject(LoadingService);
}
+6 -13
View File
@@ -23,9 +23,7 @@
}
:host-context(.modern-modal-footer) button.ti-btn-primary-full,
:host-context(.modern-modal-footer) button.ti-btn-success-full,
:host-context(.onboarding-action-footer) button.ti-btn-primary-full,
:host-context(.onboarding-action-footer) button.ti-btn-success-full {
:host-context(.modern-modal-footer) button.ti-btn-success-full {
position: relative;
overflow: hidden;
border: none;
@@ -36,17 +34,13 @@
}
:host-context(.modern-modal-footer) button.ti-btn-primary-full:hover:not(:disabled),
:host-context(.modern-modal-footer) button.ti-btn-success-full:hover:not(:disabled),
:host-context(.onboarding-action-footer) button.ti-btn-primary-full:hover:not(:disabled),
:host-context(.onboarding-action-footer) button.ti-btn-success-full:hover:not(:disabled) {
:host-context(.modern-modal-footer) button.ti-btn-success-full:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 12px 28px rgba(37, 99, 235, .45);
}
:host-context(.modern-modal-footer) button.ti-btn-primary-full::before,
:host-context(.modern-modal-footer) button.ti-btn-success-full::before,
:host-context(.onboarding-action-footer) button.ti-btn-primary-full::before,
:host-context(.onboarding-action-footer) button.ti-btn-success-full::before {
:host-context(.modern-modal-footer) button.ti-btn-success-full::before{
content: "";
position: absolute;
top: 0;
@@ -58,21 +52,20 @@
animation: saveButtonShine 2s infinite;
}
:host-context(.modern-modal-footer) button:disabled,
:host-context(.onboarding-action-footer) button:disabled {
:host-context(.modern-modal-footer) button:disabled {
opacity: .55;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
:host-context(.onboarding-action-footer) button.onboarding-nav-btn {
:host-context(.modern-modal-footer) button.onboarding-nav-btn {
height: 34px;
padding: 0 12px;
font-size: 12px;
}
:host-context(.onboarding-action-footer) button.onboarding-save-btn:hover:not(:disabled) {
:host-context(.modern-modal-footer) button.onboarding-save-btn:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 12px 28px rgba(37, 99, 235, .45);
}
@@ -0,0 +1,134 @@
import { DestroyRef, inject, Injectable, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject, Observable, of } from 'rxjs';
import { switchMap, catchError, tap, debounceTime } from 'rxjs/operators';
import { DataTableQueryState } from './data-table-query.state';
import {
DataTablePageEvent,
DataTableQuery,
DataTableRecord,
DataTableResult,
DataTableSortEvent
} from './data-table.types';
export interface DataTableStoreOptions<TItem, TRow extends DataTableRecord> {
fetcher: (query: DataTableQuery) => Observable<DataTableResult<TItem>>;
mapRow?: (item: TItem, serialNumber: number, query: DataTableQuery) => TRow;
onError?: (error: unknown) => void;
}
@Injectable()
export class DataTableStore<TItem, TRow extends DataTableRecord = TItem & DataTableRecord> {
private readonly destroyRef = inject(DestroyRef);
private readonly querySubject$ = new Subject<DataTableQuery>();
readonly queryState = new DataTableQueryState();
readonly rows = signal<TRow[]>([]);
readonly totalRecords = signal<number>(0);
readonly filteredRecords = signal<number>(0);
readonly loading = signal<boolean>(false);
readonly showModal = signal<boolean>(false);
readonly modalMode = signal<'create' | 'edit' | 'view'>('create');
readonly selectedItem = signal<TItem | null>(null);
readonly saving = signal<boolean>(false);
initialize(options: DataTableStoreOptions<TItem, TRow>): void {
this.querySubject$
.pipe(
debounceTime(50),
tap(() => this.loading.set(true)),
switchMap(query =>
options.fetcher(query).pipe(
catchError(err => {
this.rows.set([]);
this.totalRecords.set(0);
this.filteredRecords.set(0);
if (options.onError) {
options.onError(err);
}
return of(null);
})
)
),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(response => {
this.loading.set(false);
if (!response) return;
const currentQuery = this.queryState.getQuery();
if (response.draw !== currentQuery.draw) return;
const mappedRows: TRow[] = response.rows.map((item, index) => {
const serialNumber = (currentQuery.page - 1) * currentQuery.pageSize + index + 1;
if (options.mapRow) {
return options.mapRow(item, serialNumber, currentQuery);
}
return {
...(item as unknown as TRow),
serialNumber
};
});
this.rows.set(mappedRows);
this.totalRecords.set(response.total);
this.filteredRecords.set(response.filtered);
});
this.load(this.queryState.getQuery());
}
load(query: DataTableQuery): void {
this.querySubject$.next(query);
}
onSearch(value: string): void {
const query = this.queryState.setSearch(value.trim());
this.load(query);
}
onPageChange(event: DataTablePageEvent): void {
const query = this.queryState.setPage(event);
this.load(query);
}
onSortChange(event: DataTableSortEvent): void {
const query = this.queryState.setSort(event);
this.load(query);
}
refresh(): void {
const query = this.queryState.getQuery();
this.load(query);
}
reset(): void {
const query = this.queryState.reset();
this.load(query);
}
openCreateModal(): void {
this.modalMode.set('create');
this.selectedItem.set(null);
this.showModal.set(true);
}
openEditModal(item: TItem): void {
this.modalMode.set('edit');
this.selectedItem.set(item);
this.showModal.set(true);
}
openViewModal(item: TItem): void {
this.modalMode.set('view');
this.selectedItem.set(item);
this.showModal.set(true);
}
closeModal(): void {
if (this.saving()) return;
this.showModal.set(false);
this.selectedItem.set(null);
}
}
@@ -1,15 +0,0 @@
<!-- Footer Start -->
<footer
class="footer mt-auto xl:ps-[15rem] font-normal font-inter bg-white text-defaultsize leading-normal text-[0.813] shadow-[0_0_0.4rem_rgba(0,0,0,0.1)] dark:!bg-bodybg py-4 text-center">
<div class="container">
<span class=" dark:text-defaulttextcolor/50"> Copyright © <span id="year">{{fullyear}}</span> <a
href="javascript:void(0);" class="text-defaulttextcolor font-semibold dark:text-defaulttextcolor"> Ynex</a>.
Designed with <span class="bi bi-heart-fill text-danger"></span> by <a href="javascript:void(0);">
<span class="font-semibold text-primary underline">Spruko</span>
</a> All
rights
reserved
</span>
</div>
</footer>
<!-- Footer End -->
@@ -1,13 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.html',
styleUrl: './footer.scss'
})
export class Footer {
fullyear=new Date().getFullYear()
}
@@ -1,524 +0,0 @@
<!-- Start::Header -->
<header class="app-header">
<nav class="main-header !h-[3.75rem]" aria-label="Global">
<div class="main-header-container ps-[0.725rem] pe-[1rem] ">
<div class="header-content-left">
<!-- Start::header-element -->
<div class="header-element">
<div class="horizontal-logo">
<a routerLink="/dashboards/crm" class="header-logo">
<img src="./assets/images/brand-logos/desktop-logo.png" alt="logo" class="desktop-logo">
<img src="./assets/images/brand-logos/toggle-logo.png" alt="logo" class="toggle-logo">
<img src="./assets/images/brand-logos/desktop-dark.png" alt="logo" class="desktop-dark">
<img src="./assets/images/brand-logos/toggle-dark.png" alt="logo" class="toggle-dark">
<img src="./assets/images/brand-logos/desktop-white.png" alt="logo" class="desktop-white">
<img src="./assets/images/brand-logos/toggle-white.png" alt="logo" class="toggle-white">
</a>
</div>
</div>
<!-- End::header-element -->
<!-- Start::header-element -->
<div class="header-element md:px-[0.325rem] !items-center">
<!-- Start::header-link -->
<a aria-label="Hide Sidebar"
class="sidemenu-toggle animated-arrow hor-toggle horizontal-navtoggle inline-flex items-center focus-visible:outline-none"
(click)="toggleSidebar()" href="javascript:void(0);"><span></span></a>
<!-- End::header-link -->
</div>
<!-- End::header-element -->
</div>
<div class="header-content-right">
<div class="header-element py-[1rem] md:px-[0.65rem] px-2 header-search">
<button aria-label="button" type="button" data-hs-overlay="#search-modal"
class="inline-flex flex-shrink-0 justify-center items-center gap-2 rounded-full font-medium focus:ring-offset-0 focus:ring-offset-white transition-all text-xs dark:bg-bgdark dark:hover:bg-black/20 dark:text-[#8c9097] dark:text-white/50 dark:hover:text-white dark:focus:ring-white/10 dark:focus:ring-offset-white/10">
<i class="bx bx-search-alt-2 header-link-icon"></i>
</button>
</div>
<!-- start header country -->
<div
class="header-element py-[1rem] md:px-[0.65rem] px-2 header-country hs-dropdown ti-dropdown hidden sm:block [--placement:bottom-right] rtl:[--placement:bottom-left]">
<button id="dropdown-flag" type="button"
class="hs-dropdown-toggle ti-dropdown-toggle !p-0 flex-shrink-0 !border-0 !rounded-full !shadow-none">
<img src="./assets/images/flags/us_flag.jpg" alt="flag-img" class="h-[1.25rem] w-[1.25rem] rounded-full">
</button>
<div class="hs-dropdown-menu ti-dropdown-menu min-w-[10rem] hidden !-mt-3" aria-labelledby="dropdown-flag">
<div class="ti-dropdown-divider divide-y divide-gray-200 dark:divide-white/10">
<div class="py-2 first:pt-0 last:pb-0">
@for (lang of headeData.languages; track $index) {
<div class="ti-dropdown-item !p-[0.65rem] hover:cursor-pointer">
<div class="flex items-center space-x-2 w-full">
<div class="h-[1.375rem] w-[1.375rem] flex items-center rounded-full">
<img [src]="lang.flag" [alt]="lang.name + ' flag'" class="h-[1rem] w-[1rem] rounded-full">
</div>
<div>
<p class="!text-[0.8125rem] font-medium">
{{ lang.name }}
</p>
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
<!-- end header country -->
<!-- light and dark theme -->
<div class="header-element header-theme-mode hidden !items-center sm:block !py-[1rem] md:!px-[0.65rem] px-2">
<a aria-label="anchor"
class="hs-dark-mode-active:hidden flex hs-dark-mode group flex-shrink-0 justify-center items-center gap-2 rounded-full font-medium transition-all text-xs dark:bg-bgdark dark:hover:bg-black/20 dark:text-[#8c9097] dark:text-white/50 dark:hover:text-white dark:focus:ring-white/10 dark:focus:ring-offset-white/10"
href="javascript:void(0);" (click)="updateTheme('dark')">
<i class="bx bx-moon header-link-icon"></i>
</a>
<a aria-label="anchor"
class="hs-dark-mode-active:flex hidden hs-dark-mode group flex-shrink-0 justify-center items-center gap-2 rounded-full font-medium text-defaulttextcolor transition-all text-xs dark:bg-bodybg dark:bg-bgdark dark:hover:bg-black/20 dark:text-[#8c9097] dark:text-white/50 dark:hover:text-white dark:focus:ring-white/10 dark:focus:ring-offset-white/10"
href="javascript:void(0);" (click)="updateTheme('light')">
<i class="bx bx-sun header-link-icon"></i>
</a>
</div>
<!-- End light and dark theme -->
<!-- Header Cart item -->
<div
class="header-element cart-dropdown hs-dropdown ti-dropdown md:!block !hidden py-[1rem] md:px-[0.65rem] px-2 [--placement:bottom-right] [--auto-close:true] rtl:[--placement:bottom-left] ">
<button id="dropdown-cart" type="button"
class="hs-dropdown-toggle relative ti-dropdown-toggle !p-0 !border-0 flex-shrink-0 !rounded-full !shadow-none align-middle text-xs">
<i class="bx bx-cart header-link-icon"></i>
<span class="flex absolute h-5 w-5 -top-[0.25rem] end-0 -me-[0.6rem]">
<span
class="relative inline-flex rounded-full h-[14.7px] w-[14px] text-[0.625rem] bg-primary text-white justify-center items-center"
id="cart-icon-badge">{{ cartItemCount }}</span>
</span>
</button>
<div
class="main-header-dropdown bg-white !-mt-3 !p-0 hs-dropdown-menu ti-dropdown-menu w-[22rem] border-0 border-defaultborder hidden"
aria-labelledby="dropdown-cart">
<div class="ti-dropdown-header !bg-transparent flex justify-between items-center !m-0 !p-4">
<p class="text-defaulttextcolor !text-[1.0625rem] dark:text-[#8c9097] dark:text-white/50 font-semibold">
Cart Items</p>
<a href="javascript:void(0);"
class="font-[600] py-[0.25/2rem] px-[0.45rem] rounded-[0.25rem] bg-success/10 text-success text-[0.75em] "
id="cart-data">{{ cartItemCount }} Items</a>
</div>
<div>
<hr class="dropdown-divider dark:border-white/10">
</div>
@if(headeData.cartItems.length>0){
<ul class="list-none mb-0" id="header-cart-items-scroll">
@for (item of headeData.cartItems; track $index) {
<li class="ti-dropdown-item border-b dark:border-defaultborder/10 border-defaultborder" [id]="item.id">
<div class="flex items-start cart-dropdown-item">
<img [src]="item.img" alt="img"
class="!h-[1.75rem] !w-[1.75rem] leading-[1.75rem] text-[0.65rem] rounded-[50%] br-5 me-3">
<div class="grow">
<div class="flex items-start justify-between mb-0">
<div
class="mb-0 !text-[0.8125rem] text-[#232323] font-semibold dark:text-[#8c9097] dark:text-white">
<a href="javascript:void(0);">{{ item.name }}</a>
</div>
<div class="inline-flex">
<span class="text-black mb-1 font-semibold! dark:text-white ">{{ item.price }}</span>
<a aria-label="anchor" (click)="removeRow(item.id,$event)"
class="header-cart-remove ltr:float-right rtl:float-left dropdown-item-close">
<i class="ti ti-trash"></i>
</a>
</div>
</div>
<div class="min-w-fit flex items-start justify-between">
<ul class="header-product-item dark:text-white/50 flex gap-2">
@for (tag of item.tags; track $index) {
<li>{{ tag }}</li>
}
@if (item.freeShipping) {
<li>
<span
class="font-[600] py-[0.25rem] px-[0.45rem] rounded-[0.25rem] bg-pink/10 text-pink text-[0.625rem]">
Free shipping
</span>
</li>
}
</ul>
</div>
</div>
</div>
</li>
}
</ul>
@if(cartItemCount > 0){
<div class="p-3 empty-header-item ">
<div class="grid">
<a href="javascript:void(0);" class="w-full ti-btn ti-btn-primary-full p-2">Proceed to
checkout</a>
</div>
</div>
}
}
@else {
<div class="p-[3rem] empty-item ">
<div class="text-center">
<span class="!w-[4rem] !h-[4rem] !leading-[4rem] rounded-[50%] avatar bg-warning/10 !text-warning">
<i class="ri-shopping-cart-2-line text-[2rem]"></i>
</span>
<h6 class="font-bold mb-1 mt-3 text-[1rem] text-defaulttextcolor dark:text-white">Your Cart is Empty
</h6>
<span
class="mb-3 !font-normal text-[0.8125rem] block text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50">Add
some items to make me happy :)</span>
<a href="javascript:void(0);"
class="ti-btn ti-btn-primary btn-wave ti-btn-wave btn-sm m-1 !text-[0.75rem] !py-[0.25rem] !px-[0.5rem]"
data-abc="true">continue shopping <i class="bi bi-arrow-right ms-1"></i></a>
</div>
</div>
}
</div>
</div>
<!--End Header cart item -->
<!--Header Notifictaion -->
<div
class="header-element py-[1rem] md:px-[0.65rem] px-2 notifications-dropdown header-notification hs-dropdown ti-dropdown !hidden md:!block [--placement:bottom-right] rtl:[--placement:bottom-left]">
<button id="dropdown-notification" type="button"
class="hs-dropdown-toggle relative ti-dropdown-toggle !p-0 !border-0 flex-shrink-0 !rounded-full !shadow-none align-middle text-xs">
<i class="bx bx-bell header-link-icon text-[1.125rem]"></i>
<span class="flex absolute h-5 w-5 -top-[0.25rem] end-0 -me-[0.6rem]">
<span
class="animate-slow-ping absolute inline-flex -top-[2px] -start-[2px] h-full w-full rounded-full bg-secondary/40 opacity-75"></span>
<span
class="relative inline-flex justify-center items-center rounded-full h-[14.7px] w-[14px] bg-secondary text-[0.625rem] text-white"
id="notification-icon-badge">{{notificationItemCount}}</span>
</span>
</button>
<div
class="main-header-dropdown !-mt-3 !p-0 hs-dropdown-menu ti-dropdown-menu bg-white !w-[22rem] border-0 border-defaultborder dark:!border-defaultborder/10 hidden !m-0"
aria-labelledby="dropdown-notification">
<div class="ti-dropdown-header !m-0 !p-4 !bg-transparent flex justify-between items-center">
<p
class="mb-0 text-[1.0625rem] text-defaulttextcolor font-semibold dark:text-[#8c9097] dark:text-white/50">
Notifications</p>
<span
class="text-[0.75em] py-[0.25rem/2] px-[0.45rem] font-[600] rounded-sm bg-secondary/10 text-secondary"
id="notifiation-data">{{notificationItemCount}} Unread</span>
</div>
<div class="dropdown-divider "></div>
<ul class="list-none !m-0 !p-0 end-0" id="header-notification-scroll" (click)="handleCardClick($event)">
@for (item of headeData.notifications; track $index) {
<li class="ti-dropdown-item dropdown-item !block">
<div class="flex items-start">
<div class="pe-2">
<span
[class]="`inline-flex justify-center items-center !w-[2.5rem] !h-[2.5rem] !leading-[2.5rem] !text-[0.8rem] rounded-[50%] ${item.colorClass} ${item.bgClass}`">
<i [class]="`ti ${item.icon} text-[1.125rem]`"></i>
</span>
</div>
<div class="grow flex items-center justify-between">
<div>
<p class="mb-0 text-defaulttextcolor dark:text-white text-[0.8125rem] font-semibold">
<a href="javascript:void(0);">{{ item.title }} </a>
@if(item.orderId){
<span [class]="item.colorClass">ID:{{item.orderId}}</span>
}
</p>
<span class="text-[#8c9097] dark:text-white/50 font-normal text-[0.75rem]">
{{ item.description }}
</span>
</div>
<div>
<a href="javascript:void(0);" (click)="removeNotification(item.id, $event)"
class="min-w-fit text-[#8c9097] dark:text-white/50 me-1">
<i class="ti ti-x text-[1rem]"></i>
</a>
</div>
</div>
</div>
</li>
}
</ul>
@if (headeData.notifications.length > 0){
<div class="p-4 empty-header-item1 mt-2 border-t border-defaultborder dark:border-defaultborder/10">
<div class="grid">
<a href="javascript:void(0);" class="ti-btn ti-btn-primary-full !m-0 w-full p-2">View All</a>
</div>
</div>
}
@if (headeData.notifications.length === 0){
<div class="p-[3rem] empty-item1 ">
<div class="text-center">
<span
class="!h-[4rem] !w-[4rem] avatar !leading-[4rem] !rounded-full !bg-secondary/10 !text-secondary">
<i class="ri-notification-off-line text-[2rem] "></i>
</span>
<h6 class="font-semibold mt-3 text-defaulttextcolor dark:text-white text-[1rem]">No New Notifications
</h6>
</div>
</div>
}
</div>
</div>
<!--End Header Notifictaion -->
<!-- Related Apps -->
<div
class="header-element header-apps dark:text-[#8c9097] dark:text-white/50 py-[1rem] md:px-[0.65rem] px-2 hs-dropdown ti-dropdown md:!block !hidden [--placement:bottom-left] [--auto-close:true]">
<button aria-label="button" id="dropdown-apps" type="button"
class="hs-dropdown-toggle ti-dropdown-toggle !p-0 !border-0 flex-shrink-0 !rounded-full !shadow-none text-xs">
<i class="bx bx-grid-alt header-link-icon text-[1.125rem]"></i>
</button>
<div
class="main-header-dropdown !-mt-3 hs-dropdown-menu ti-dropdown-menu !w-[22rem] border-0 border-defaultborder hidden"
aria-labelledby="dropdown-apps">
<div class="p-4">
<div class="flex items-center justify-between">
<p
class="mb-0 text-defaulttextcolor text-[1.0625rem] dark:text-[#8c9097] dark:text-white/50 font-semibold">
Related Apps</p>
</div>
</div>
<div class="dropdown-divider mb-0"></div>
<div class="ti-dropdown-divider divide-y divide-gray-200 dark:divide-white/10 main-header-shortcuts p-2"
id="header-shortcut-scroll">
<div class="grid grid-cols-3 gap-2">
@for (app of headeData.relatedApps; track $index) {
<div class="">
<a href="javascript:void(0);"
class="p-4 items-center related-app block text-center rounded-sm hover:!bg-gray-100 dark:hover:!bg-black/20">
<div>
<img [src]="app.img" [alt]="app.alt"
class="!h-[1.75rem] !w-[1.75rem] text-2xl avatar text-primary flex justify-center items-center mx-auto">
<div class="text-[0.75rem] text-defaulttextcolor dark:text-[#8c9097] dark:text-white/50">
{{ app.name }}
</div>
</div>
</a>
</div>
}
</div>
</div>
<div class="p-4 first:pt-0 border-t border-defaultborder dark:border-defaultborder/10">
<a class="w-full ti-btn ti-btn-primary-full p-2 !m-0" href="javascript:void(0);">
View All
</a>
</div>
</div>
</div>
<!--End Related Apps -->
<!-- Fullscreen -->
<div class="header-element header-fullscreen py-[1rem] md:px-[0.65rem] px-2" appFullscreen>
<!-- Start::header-link -->
<a aria-label="anchor" (click)="toggleFullscreen()" href="javascript:void(0);"
class="inline-flex flex-shrink-0 justify-center items-center gap-2 !rounded-full font-medium dark:hover:bg-black/20 dark:text-[#8c9097] dark:text-white/50 dark:hover:text-white dark:focus:ring-white/10 dark:focus:ring-offset-white/10">
@if (isFullscreen) {
<i class="bx bx-exit-fullscreen full-screen-close header-link-icon hidden"></i>
} @else {
<i class="bx bx-fullscreen full-screen-open header-link-icon"></i>
}
</a>
<!-- End::header-link -->
</div>
<!-- End Full screen -->
<!-- Header Profile -->
<div
class="header-element md:!px-[0.65rem] px-2 hs-dropdown !items-center ti-dropdown [--placement:bottom-right]">
<button id="dropdown-profile" type="button"
class="hs-dropdown-toggle ti-dropdown-toggle !gap-2 !p-0 flex-shrink-0 !rounded-full !shadow-none text-xs align-middle !border-0 !shadow-transparent hover:!bg-transparent ">
<img class="inline-block rounded-full " src="./assets/images/faces/9.jpg" width="32" height="32"
alt="Image Description">
<div class="md:block hidden dropdown-profile">
<p class="font-semibold mb-0 leading-none text-[#536485] text-[0.813rem] ">{{ getDisplayName() }}</p>
<span class="opacity-[0.7] font-normal text-[#536485] block text-[0.6875rem] ">{{ getPrimaryRole() }}</span>
</div>
</button>
<div
class="hs-dropdown-menu ti-dropdown-menu !-mt-3 border-0 w-[11rem] !p-0 border-defaultborder hidden main-header-dropdown pt-0 overflow-hidden header-profile-dropdown dropdown-menu-end"
aria-labelledby="dropdown-profile">
<ul class="text-defaulttextcolor font-medium dark:text-[#8c9097] dark:text-white/50">
@for (item of headeData.menuItems; track $index) {
<li>
<a href="javascript:void(0);" (click)="handleProfileItemClick(item, $event)"
class="w-full ti-dropdown-item !text-[0.8125rem] !gap-x-0 !p-[0.65rem] !inline-flex">
<i class="ti {{ item.icon }} text-[1.125rem] me-2 opacity-[0.7]"></i>
{{ item.label }}
@if (item.badge) {
<span
class="!py-1 !px-[0.45rem] !font-semibold !rounded-sm text-success text-[0.75em] bg-success/10 ms-auto">
{{ item.badge }}
</span>
}
</a>
</li>
}
</ul>
</div>
</div>
<!-- End Header Profile -->
<!-- Switcher Icon -->
<div class="header-element md:px-[0.48rem]">
<button aria-label="button" type="button"
class="hs-dropdown-toggle switcher-icon focus-visible:outline-none inline-flex flex-shrink-0 justify-center items-center gap-2 rounded-full font-medium align-middle transition-all text-xs dark:text-[#8c9097] dark:text-white/50 dark:hover:text-white dark:focus:ring-white/10 dark:focus:ring-offset-white/10"
data-hs-overlay="#hs-overlay-switcher" data-hs-overlay-options='{
"backdrop": true,
"isClosePrev": true
}'>
<i class="bx bx-cog header-link-icon animate-spin-slow !p-0 !block"></i>
</button>
</div>
<!-- Switcher Icon -->
<!-- End::header-element -->
</div>
</div>
</nav>
</header>
<!-- End::Header -->
<div id="search-modal"
class="hs-overlay ti-modal hidden mt-[1.75rem] hs-overlay-backdrop-open:!bg-[#32325180] dark:hs-overlay-backdrop-open:!bg-[#323251cc] pointer-events-none">
<div class="ti-modal-box pointer-events-auto">
<div class="ti-modal-content !border !border-defaultborder dark:!border-defaultborder/10 !rounded-[0.5rem]">
<div class="ti-modal-body">
<div class="input-group border-[2px] border-primary rounded-[0.25rem] w-full flex">
<a aria-label="anchor" href="javascript:void(0);"
class="input-group-text flex items-center bg-light border-e-[#dee2e6] !py-[0.375rem] border-0! !px-[0.75rem] !rounded-none !text-[0.875rem]"
id="Search-Grid">
<i class="fe fe-search header-link-icon text-[0.875rem]"></i>
</a>
<input type="search" class="form-control border-0! px-2 !text-[0.8rem] w-full focus:ring-transparent"
placeholder="Search" aria-label="Username" [(ngModel)]="text" (keyup)="Search(text)" type="search">
<a aria-label="anchor" href="javascript:void(0);"
class="flex items-center input-group-text bg-light !py-[0.375rem] !px-[0.75rem]" id="voice-search"><i
class="fe fe-mic header-link-icon"></i></a>
<div class="hs-dropdown ti-dropdown">
<a aria-label="anchor" href="javascript:void(0);"
class="flex items-center hs-dropdown-toggle ti-dropdown-toggle border-0! border-defaultborder dark:border-defaultborder/10 btn btn-light btn-icon !bg-light !py-[0.375rem] !rounded-none !px-[0.75rem] text-[0.95rem] h-[2.413rem] w-[2.313rem]">
<i class="fe fe-more-vertical"></i>
</a>
<ul class="absolute hs-dropdown-menu ti-dropdown-menu !-mt-2 !p-0 hidden">
<li><a
class="ti-dropdown-item flex text-defaulttextcolor dark:text-defaulttextcolor/70 !py-[0.5rem] !px-[0.9375rem] !text-[0.8125rem] font-medium"
href="javascript:void(0);">Action</a></li>
<li><a
class="ti-dropdown-item flex text-defaulttextcolor dark:text-defaulttextcolor/70 !py-[0.5rem] !px-[0.9375rem] !text-[0.8125rem] font-medium"
href="javascript:void(0);">Another action</a></li>
<li><a
class="ti-dropdown-item flex text-defaulttextcolor dark:text-defaulttextcolor/70 !py-[0.5rem] !px-[0.9375rem] !text-[0.8125rem] font-medium"
href="javascript:void(0);">Something else here</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a
class="ti-dropdown-item flex text-defaulttextcolor dark:text-defaulttextcolor/70 !py-[0.5rem] !px-[0.9375rem] !text-[0.8125rem] font-medium"
href="javascript:void(0);">Separated link</a></li>
</ul>
</div>
</div>
<div class="mt-5">
<p class="font-normal text-[#8c9097] dark:text-white/50 text-[0.813rem] dark:text-gray-200 mb-2">Are You
Looking For...</p>
@if (menuItems && menuItems.length > 0) {
<div
class=" w-full mb-2! bg-white dark:!bg-bodybg shadow-md top-[2.5rem] !rounded-s rounded-md border border-defaultborder dark:!border-defaultborder/10">
<ul class="divide-y divide-gray-200">
@for (menuItem of menuItems | slice:0:5;track $index) {
<li [routerLink]="menuItem?.path"
class="p-3 dark:!border-defaultborder/10 cursor-pointer flex items-center hover:bg-primary hover:text-white "
(click)="clearSearch()">
<i class="fe fe-chevrons-right mx-1 customsearcharrow rtl:rotate-[180deg]"></i>
<span class="truncate">{{ menuItem?.title }}</span>
</li>
}
</ul>
</div>
}
@for (tag of headeData.searchTags; track tag.id) {
<span class="search-tags text-[0.75rem] !py-[0rem] !px-[0.55rem] border-defaultborder dark:!border-defaultborder/10">
<i class="fe {{tag.icon}} me-2"></i> {{tag.label}}
<a href="javascript:void(0);" (click)="removeAlert(headeData.searchTags,tag.id)" class="tag-addon header-remove-btn">
<span class="sr-only">Remove badge</span><i class="fe fe-x text-[10px]"></i>
</a>
</span>
}
</div>
<div class="my-[1.5rem]">
<p class="font-normal text-[#8c9097] dark:text-white/50 text-[0.813rem] mb-2">Recent Search :</p>
@for (alert of headeData.alertItems; track alert.id) {
<div role="alert"
class="!p-2 border border-defaultborder dark:border-defaultborder/10 rounded-[0.3125rem] flex items-center text-defaulttextcolor dark:text-defaulttextcolor/70 !mb-2 !text-[0.8125rem] alert">
<a [routerLink]="alert.link">
<span>{{ alert.label }}</span>
</a>
<a (click)="removeAlert(headeData.alertItems,alert.id)" aria-label="anchor"
class="ms-auto leading-none cursor-pointer" href="javascript:void(0);">
<i class="fe fe-x !text-[0.8125rem] text-[#8c9097] dark:text-white/50"></i>
</a>
</div>
}
</div>
</div>
<div
class="ti-modal-footer !border !border-defaultborder dark:!border-defaultborder/10 !py-[1rem] !px-[1.25rem]">
<div class="inline-flex rounded-md shadow-sm">
<button type="button"
class="ti-btn-group focus-visible:outline-none !px-[0.75rem] !py-[0.45rem] rounded-s-[0.25rem] !rounded-e-none ti-btn-primary !text-[0.75rem] dark:border-white/10">
Search
</button>
<button type="button"
class="ti-btn-group ti-btn-primary-full rounded-e-[0.25rem] dark:border-white/10 !text-[0.75rem] !rounded-s-none !px-[0.75rem] !py-[0.45rem]">
Clear Recents
</button>
</div>
</div>
</div>
</div>
</div>
-318
View File
@@ -1,318 +0,0 @@
import { Component, DOCUMENT, ElementRef, Renderer2, inject } from '@angular/core';
import { Menu, NavService } from '../../services/nav.service';
import * as headeData from "./headerdata"
import { AppStateService } from '../../services/app-state.service';
import { Subscription } from 'rxjs';
import { Router, RouterLink } from '@angular/router';
import { FullscreenDirective } from '../../directives/fullscreen.directive';
import { FormsModule } from '@angular/forms';
import { SlicePipe } from '@angular/common';
import { AuthService } from '../../../core/auth/auth.service';
interface Item {
id: number;
name: string;
type: string;
title: string;
// Add other properties as needed
}
declare const HSStaticMethods: any;
@Component({
selector: 'app-header',
templateUrl: './header.html',
styleUrls: ['./header.scss'],
imports: [RouterLink, FullscreenDirective, FormsModule, SlicePipe]
})
export class Header {
headeData = headeData;
public menuItems!: Menu[];
public menuitemsSubscribe$!: Subscription;
public NavServices = inject(NavService)
private appStateService = inject(AppStateService)
private readonly authService = inject(AuthService);
private readonly router = inject(Router);
readonly currentUser = this.authService.currentUserSignal;
Selector = (selector: any) => document.querySelector(selector);
SelectorAll = (selector: any) => document.querySelectorAll(selector);
private doc = inject(DOCUMENT);
toggleSidebar() {
const html = this.doc.documentElement;
// Check the window width
if (window.innerWidth <= 992) {
let dataToggled = html.getAttribute("data-toggled");
if (dataToggled == "open") {
html.setAttribute("data-toggled", "close");
} else {
html.setAttribute("data-toggled", "open");
}
}
else {
let menuNavLayoutType = html.getAttribute("data-nav-style");
let verticalStyleType = html.getAttribute("data-vertical-style");
if (menuNavLayoutType) {
let dataToggled = html.getAttribute("data-toggled");
if (dataToggled) {
html.removeAttribute("data-toggled");
} else {
html.setAttribute(
"data-toggled",
menuNavLayoutType + "-closed",
);
}
} else if (verticalStyleType) {
let dataToggled = html.getAttribute("data-toggled");
if (verticalStyleType == "doublemenu") {
if (
html.getAttribute("data-toggled") === "double-menu-open" && this.Selector(".double-menu-active")) {
html.setAttribute("data-toggled", "double-menu-close");
} else {
if (this.Selector(".double-menu-active")) { html.setAttribute("data-toggled", "double-menu-open"); }
}
} else if (dataToggled) {
html.removeAttribute("data-toggled");
} else {
switch (verticalStyleType) {
case "closed":
html.setAttribute(
"data-toggled",
"close-menu-close",
);
break;
case "icontext":
html.setAttribute(
"data-toggled",
"icon-text-close",
);
break;
case "overlay":
html.setAttribute(
"data-toggled",
"icon-overlay-close",
);
break;
case "detached":
html.setAttribute("data-toggled", "detached-close");
break;
default:
}
}
}
}
}
public items: Menu[] = []; // Your full menu data (source)
public text: string = '';
public SearchResultEmpty: boolean = false;
public isDropdownVisible: boolean = false;
Search(searchText: string) {
// 2. Safety Check: If search is empty or source data hasn't loaded
if (!searchText || !this.items) {
this.menuItems = [];
this.SearchResultEmpty = false;
return;
}
const results: Menu[] = [];
const query = searchText.toLowerCase().trim();
// 3. Deep search through 3 levels of menu
this.items.forEach((level1: Menu) => {
// Check Level 1
if (level1.title?.toLowerCase().includes(query)) {
results.push(level1);
}
// Check Level 2 (Children)
if (level1.children) {
level1.children.forEach((level2: Menu) => {
if (level2.title?.toLowerCase().includes(query)) {
results.push(level2);
}
// Check Level 3 (Sub-children)
if (level2.children) {
level2.children.forEach((level3: Menu) => {
if (level3.title?.toLowerCase().includes(query)) {
results.push(level3);
}
});
}
});
}
});
// 4. Update UI State
this.menuItems = results;
this.SearchResultEmpty = results.length === 0;
}
// Used to clear previous search result
clearSearch() {
const headerSearch = this.Selector('.header-search');
if (headerSearch) {
headerSearch.classList.remove('searchdrop');
}
this.text = '';
this.menuItems = [];
this.SearchResultEmpty = false;
return this.text, this.menuItems;
}
updateTheme(theme: string) {
this.appStateService.updateState({ theme, menuColor: theme, headerColor: theme });
if (theme == 'light') {
this.appStateService.updateState({ theme, themeBackground: '', headerColor: 'light', menuColor: 'dark' });
let html = document.querySelector('html');
html?.style.removeProperty('--color-bodybg');
html?.style.removeProperty('--color-bodybg2');
html?.style.removeProperty('--color-light');
html?.style.removeProperty('--color-formcontrolbg');
html?.style.removeProperty('--color-inputborder');
html?.style.removeProperty('--color-gray3');
if (window.innerWidth <= 992) {
html?.setAttribute('data-toggled', 'close');
}
}
if (theme == 'dark') {
this.appStateService.updateState({ theme, themeBackground: '', headerColor: 'dark', menuColor: 'dark' });
let html = document.querySelector('html');
html?.style.removeProperty('--color-bodybg');
html?.style.removeProperty('--color-bodybg2');
html?.style.removeProperty('--color-light');
html?.style.removeProperty('--color-formcontrolbg');
html?.style.removeProperty('--color-inputborder');
html?.style.removeProperty('--color-gray3');
if (window.innerWidth <= 992) {
html?.setAttribute('data-toggled', 'close');
}
}
}
cartItemCount = this.headeData.cartItems.length;
notificationItemCount = this.headeData.notifications.length;
handleCardClick(event: MouseEvent) {
// Prevent the click event from propagating to the container
event.stopPropagation();
}
removeRow(itemId: string,event: MouseEvent) {
const index = this.headeData.cartItems.findIndex(i => i.id === itemId);
if (index !== -1) {
this.headeData.cartItems.splice(index, 1);
}
this.updateCartItemCount();
event.stopPropagation();
}
updateCartItemCount() {
this.cartItemCount = this.headeData.cartItems.length;
}
removeNotification(id: number, event: Event): void {
event.preventDefault(); // Prevent link navigation
this.headeData.notifications.splice(
this.headeData.notifications.findIndex(item => item.id === id),
1
);
this.updatenotificationsItemCount();
}
updatenotificationsItemCount() {
this.notificationItemCount = this.headeData.notifications.length;
}
isFullscreen: boolean = false;
toggleFullscreen() {
this.isFullscreen = !this.isFullscreen;
}
removeAlert<T extends { id: string }>(array: T[], id: string): void {
const index = array.findIndex(item => item.id === id);
if (index !== -1) {
array.splice(index, 1);
}
}
ngOnInit(): void {
this.NavServices.items.subscribe((menuItems) => {
this.items = menuItems;
});
}
ngAfterViewInit(): void {
HSStaticMethods.autoInit();
}
getDisplayName(): string {
const email = this.currentUser()?.email?.trim();
if (email) {
const localPart = email.split('@')[0];
const formatted = localPart
.replace(/[._-]+/g, ' ')
.replace(/\s+/g, ' ')
.trim();
if (formatted) {
return formatted
.split(' ')
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
.join(' ');
}
}
return this.currentUser()?.displayName?.trim() || 'User';
}
getPrimaryRole(): string {
const roles = this.currentUser()?.roles;
if (roles && roles.length > 0) {
const role = roles[0].trim();
if (role) {
return role
.replace(/[_-]+/g, ' ')
.replace(/\s+/g, ' ')
.split(' ')
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
.join(' ');
}
}
return 'User';
}
handleProfileItemClick(item: { label?: string; link?: string | null }, event: MouseEvent): void {
event.preventDefault();
if (item.label === 'Log Out') {
this.logout();
return;
}
if (item.link) {
void this.router.navigateByUrl(item.link);
}
}
logout(): void {
this.authService.logout();
void this.router.navigate(['/auth/login']);
}
}
@@ -1,102 +0,0 @@
export const languages = [
{ name: 'English', flag: './assets/images/flags/us_flag.jpg' },
{ name: 'Spanish', flag: './assets/images/flags/spain_flag.jpg' },
{ name: 'French', flag: './assets/images/flags/french_flag.jpg' },
{ name: 'German', flag: './assets/images/flags/germany_flag.jpg' },
{ name: 'Italian', flag: './assets/images/flags/italy_flag.jpg' },
{ name: 'Russian', flag: './assets/images/flags/russia_flag.jpg' }
];
export const cartItems = [
{ id: 'row1', name: 'SomeThing Phone', price: '$1,299.00', img: './assets/images/ecommerce/jpg/1.jpg', tags: ['Metallic Blue', '6gb Ram'] },
{ id: 'row2', name: 'Stop Watch', price: '$179.29', img: './assets/images/ecommerce/jpg/3.jpg', tags: ['Analog'], freeShipping: true },
{ id: 'row3', name: 'Photo Frame', price: '$29.00', img: './assets/images/ecommerce/jpg/5.jpg', tags: ['Decorative'] },
{ id: 'row4', name: 'Kikon Camera', price: '$4,999.00', img: './assets/images/ecommerce/jpg/4.jpg', tags: ['Black', '50MM'] },
{ id: 'row5', name: 'Canvas Shoes', price: '$129.00', img: './assets/images/ecommerce/jpg/6.jpg', tags: ['Gray', 'Sports'] },
];
export const notifications = [
{
id: 1,
type: 'shipment',
icon: 'ti-gift',
colorClass: 'text-primary',
bgClass: 'bg-primary/10',
title: 'Your Order Has Been Shipped',
description: 'Order No: 123456 Has Shipped To Your Delivery Address', },
{
id: 2,
type: 'discount',
icon: 'ti-discount-2',
colorClass: 'text-secondary',
bgClass: 'bg-secondary/10',
title: 'Discount Available',
description: 'Discount Available On Selected Products', },
{
id: 3,
type: 'verify',
icon: 'ti-user-check',
colorClass: 'text-pink',
bgClass: 'bg-pink/10',
title: 'Account Has Been Verified',
description: 'Your Account Has Been Verified Successfully', },
{
id: 4,
type: 'placed',
icon: 'ti-circle-check',
colorClass: 'text-warning',
bgClass: 'bg-warning/10',
title: 'Order Placed',
description: 'Order Placed Successfully',
orderId: '#1116773'
},
{
id: 5,
type: 'delayed',
icon: 'ti-clock',
colorClass: 'text-success',
bgClass: 'bg-success/10',
title: 'Order Delayed',
description: 'Order Delayed Unfortunately',
orderId: '7731116'
}
];
export const relatedApps = [
{ name: 'Figma', img: './assets/images/apps/figma.png', alt: 'figma' },
{ name: 'Power Point', img: './assets/images/apps/microsoft-powerpoint.png', alt: 'microsoft' },
{ name: 'MS Word', img: './assets/images/apps/microsoft-word.png', alt: 'msword' },
{ name: 'Calendar', img: './assets/images/apps/calender.png', alt: 'calendar' },
{ name: 'Sketch', img: './assets/images/apps/sketch.png', alt: 'sketch' },
{ name: 'Docs', img: './assets/images/apps/google-docs.png', alt: 'docs' },
{ name: 'Google', img: './assets/images/apps/google.png', alt: 'google' },
{ name: 'Translate', img: './assets/images/apps/translate.png', alt: 'translate' },
{ name: 'Sheets', img: './assets/images/apps/google-sheets.png', alt: 'sheets' }
];
export const menuItems = [
{ label: 'Profile', link: '/pages/profile', icon: 'ti-user-circle' },
{ label: 'Inbox', link: '/pages/email/mailapp', icon: 'ti-inbox', badge: '25' },
{ label: 'Task Manager', link: '/pages/todolist', icon: 'ti-clipboard-check' },
{ label: 'Settings', link: '/pages/email/mailsettings', icon: 'ti-adjustments-horizontal' },
{ label: 'Bal: $7,12,950', link: null, icon: 'ti-wallet' }, // Special case for non-router link
{ label: 'Support', link: '/pages/chat', icon: 'ti-headset' },
{ label: 'Log Out', link: '/authentication/sign-in/cover', icon: 'ti-logout' },
];
export const searchTags = [
{ id: 'tag1', label: 'People', icon: 'fe-user' },
{ id: 'tag2', label: 'Pages', icon: 'fe-file-text' },
{ id: 'tag3', label: 'Articles', icon: 'fe-align-left' },
{ id: 'tag4', label: 'Tags', icon: 'fe-server' }
];
export const alertItems = [
{ id: 'tag5', label: 'Notifications', link: '/pages/notifications' },
{ id: 'tag6', label: 'Alerts', link: '/uielements/alerts' },
{ id: 'tag7', label: 'Mail', link: '/pages/email/mailapp' }
];
@@ -1,36 +0,0 @@
@if(childTitle){
<div class="block justify-between page-header md:flex">
<div>
<h3
class="!text-defaulttextcolor dark:!text-defaulttextcolor/70 dark:text-white dark:hover:text-white text-[1.125rem]! font-semibold">
{{childTitle}}</h3>
</div>
<ol class="flex items-center whitespace-nowrap min-w-0">
@if(parentTitle){
<li class="text-[0.813rem] sm:ps-[0.5rem] ps-0">
<a class="flex items-center text-primary hover:text-primary dark:text-primary truncate"
href="javascript:void(0);">
{{parentTitle}}
<i
class="ti ti-chevrons-right !text-defaulttextcolor dark:!text-defaulttextcolor/70 px-[0.5rem] overflow-visible rtl:rotate-180"></i>
</a>
</li>
}
@if(subParentTitle){
<li class="text-[0.813rem] ps-[0.5rem]">
<a class="flex items-center text-primary hover:text-primary dark:text-primary truncate"
href="javascript:void(0);">
{{subParentTitle}}
<i
class="ti ti-chevrons-right flex-shrink-0 !text-defaulttextcolor dark:!text-defaulttextcolor/70 px-[0.5rem] overflow-visible rtl:rotate-180"></i>
</a>
</li>
}
<li
class="text-[0.813rem] !text-defaulttextcolor dark:!text-defaulttextcolor/70 font-semibold hover:!text-primary "
aria-current="page">
{{childTitle}}
</li>
</ol>
</div>
}
@@ -1,34 +0,0 @@
import { Component, input } from '@angular/core';
import { ChildrenOutletContexts, NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs';
@Component({
selector: 'app-page-header',
templateUrl: './page-header.html',
styleUrls: ['./page-header.scss']
})
export class PageHeader {
parentTitle?: string;
subParentTitle?: string;
childTitle?: string;
constructor(
private router: Router,
private childrenOutletContexts: ChildrenOutletContexts
) {
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe(() => {
const context = this.childrenOutletContexts.getContext('primary');
const routeData = context?.route?.snapshot?.data;
if (routeData) {
this.childTitle = routeData['childTitle'] ?? '';
this.parentTitle = routeData['parentTitle'] ?? '';
this.subParentTitle = routeData['subParentTitle'] ?? '';
}
});
}
}
@@ -1,16 +0,0 @@
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-problem-details-toast',
standalone: true,
template: `
<div class="rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger">
<div class="font-semibold">{{ title }}</div>
<div>{{ detail }}</div>
</div>
`,
})
export class ProblemDetailsToast {
@Input() title = 'Request failed';
@Input() detail = 'An unexpected error occurred.';
}
@@ -1,216 +0,0 @@
<aside class="app-sidebar" id="sidebar" [ngClass]="{ 'sticky-pin': scrolled }">
<!-- Start::main-sidebar-header -->
<div class="main-sidebar-header">
<a routerLink="/dashboards/crm" class="header-logo">
<!-- <img src="./assets/images/brand-logos/desktop-logo.png" alt="logo" class="desktop-logo">
<img src="./assets/images/brand-logos/toggle-logo.png" alt="logo" class="toggle-logo">
<img src="./assets/images/brand-logos/desktop-dark.png" alt="logo" class="desktop-dark">
<img src="./assets/images/brand-logos/toggle-dark.png" alt="logo" class="toggle-dark">
<img src="./assets/images/brand-logos/desktop-white.png" alt="logo" class="desktop-white">
<img src="./assets/images/brand-logos/toggle-white.png" alt="logo" class="toggle-white"> -->
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="desktop-logo" />
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="toggle-logo" />
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="desktop-dark" />
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="toggle-dark" />
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="desktop-white" />
<img src="assets/images/brand-logos/erp-logo-icon.png" alt="ERP Logo" class="toggle-white" />
</a>
</div>
<div>
<ngx-simplebar [options]="options" class="main-sidebar" id="sidebar-scroll">
<nav class="main-menu-container nav nav-pills flex-column sub-open">
<div class="slide-left" id="slide-left" (click)="leftArrowFn()">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8191" width="24" height="24" viewBox="0 0 24 24">
<path d="M13.293 6.293 7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z"></path>
</svg>
</div>
<ul class="main-menu" [ngStyle]="{ display: 'block' }">
@for (menuItem of menuItems; track menuItem) {
<li class="slide" #activeMenuItems [ngClass]="{'slide__category':menuItem.headTitle,
'slide has-sub':menuItem.title,
'open': menuItem.active,
'active': menuItem.selected}">
<!-- head title -->
@if(menuItem.headTitle){
<span class="category-name">{{ menuItem.headTitle }}</span>
}
<!-- has-Link -->
@if (menuItem.type === 'link') {
<a class="side-menu__item" [routerLink]="!menuItem.type ? null : [menuItem.path]" routerLinkActive="active"
(click)="setNavActive($event, menuItem.path ?? '')">
@if (menuItem.icon) {
<span [appSvgReplace]="menuItem.icon" class="iconclick"></span>
}
<span class="side-menu__label">{{ menuItem.title }}
@if(menuItem.badgeValue){
<span class="badge bg-{{ menuItem.badgeClass }} text-{{menuItem.badgeText}}">{{menuItem.badgeValue
}}</span>
}
</span>
</a>
}
<!-- has-empty -->
@if (menuItem.type === 'empty') {
<a class="side-menu__item" href="javascript:;" (click)="setNavActive($event, menuItem.path ?? '')">
@if (menuItem.icon) {
<span [appSvgReplace]="menuItem.icon" class="iconclick"></span>
}
<span class="side-menu__label">{{menuItem.title}} <span class="badge bg-warning ms-2">hot</span>
</span>
</a>
}
<!-- has-Sub -->
@if (menuItem.type === 'sub') {
<a class="side-menu__item" [routerLink]="menuItem.type ? null: [menuItem.path]"
[ngClass]="{active: menuItem.selected}" (click)="toggleNavActive($event, menuItem )">
@if (menuItem.icon) {
<span [appSvgReplace]="menuItem.icon" class="iconclick"></span>
}
<span class="side-menu__label">{{menuItem.title}}
@if(menuItem.badgeValue){
<span
class="badge bg-{{ menuItem.badgeClass }} text-{{menuItem.badgeText}} ms-2">{{menuItem.badgeValue}}</span>
}
</span>
<i class="fe fe-chevron-right side-menu__angle"></i>
</a>
}
<!-- 2nd Level menu -->
@if (menuItem.children) {
<ul class="slide-menu child1"
[ngClass]="{'active':menuItem.active,'double-menu-active':menuItem.active, 'force-left' : menuItem.dirchange}"
[ngStyle]="{ display: menuItem.active ? 'block' : 'none' }">
<li class="slide side-menu__label1"><a href="javascript:void(0)">{{menuItem.title}}</a></li>
@for (childrenItem of menuItem.children; track childrenItem) {
<li class="slide" activeMenuItems [ngClass]="{'active':childrenItem.selected}" appDropdownPosition
[ngClass]="{'has-sub': childrenItem.type === 'sub','open':childrenItem.active}">
<!-- link -->
@if (childrenItem.type === 'link') {
<a class="side-menu__item" [routerLink]="!childrenItem.type ? null : [childrenItem.path] "
routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}"
(click)="setNavActive($event, childrenItem.path ?? '')">
{{childrenItem.title}}
@if(childrenItem.badgeClass){
<span class="m-1 badge bg-{{ menuItem.badgeClass }}">{{menuItem.badgeValue }}</span>
}
</a>
}
<!-- empty -->
@if (childrenItem.type === 'empty' ) {
<a class="side-menu__item " href="javascript:;" (click)="setNavActive($event, childrenItem.path ?? '')">
{{childrenItem.title }}
</a>
}
<!-- sub -->
@if (childrenItem.type === 'sub') {
<a class="side-menu__item " [ngClass]="{'active': childrenItem.selected}"
[routerLink]="childrenItem.type ? null : [childrenItem.path]"
(click)="toggleNavActive($event, childrenItem)">
<span class="">{{childrenItem.title}}</span>
<i class="fe fe-chevron-right side-menu__angle"></i>
</a>
}
<!-- 3rd Level menu -->
@if (childrenItem.children) {
<ul class="slide-menu child2" force [ngClass]="{'force-left' : childrenItem.dirchange}" [ngStyle]="{
display: childrenItem.active ? 'block' : 'none',
right: localdata['dir'] == 'rtl' ? 'auto' : '',
left: localdata['dir'] == 'rtl' ? '100%' : ''
}">
@for (childrenSubItem of childrenItem.children; track childrenSubItem) {
<li class="slide" activeMenuItems appDropdownPosition [ngClass]="{open:childrenSubItem.active}">
@if (childrenSubItem.type === 'link') {
<a class="side-menu__item" routerLinkActive="active"
[routerLink]="!childrenSubItem.type ? null : [childrenSubItem.path]"
[routerLinkActiveOptions]="{exact : true}">
{{childrenSubItem.title}}
</a>
}
@if (childrenSubItem.type === 'empty') {
<a class="side-menu__item" href="javascript:;">
{{childrenSubItem.title}}
</a>
}
@if (childrenSubItem.type === 'sub') {
<a class="side-menu__item " [ngClass]="{'active': childrenSubItem.active}"
[routerLink]="childrenSubItem.type ? null : [childrenSubItem.path]" routerLinkActive="active"
(click)="toggleNavActive($event, childrenSubItem)">
<span class="">{{childrenSubItem.title}}</span>
<i class="fe fe-chevron-right side-menu__angle"></i>
</a>
}
<!-- 3rd Level menu -->
@if (childrenSubItem.children) {
<ul class="slide-menu child2" [ngClass]="{'force-left' : childrenItem.dirchange}" [ngStyle]="{
display: childrenSubItem.active ? 'block' : 'none',
}">
@for (childrenSubItem1 of childrenSubItem.children; track childrenSubItem1) {
<li class="slide" activeMenuItems [ngClass]="{'open': childrenSubItem1.active}">
@if (childrenSubItem1.type === 'link') {
<a class="side-menu__item" routerLinkActive="active"
[routerLink]="!childrenSubItem1.type ? null : [childrenSubItem1.path]"
[routerLinkActiveOptions]="{exact : true}">
{{childrenSubItem1.title}}
</a>
}
@if (childrenSubItem1.type === 'empty') {
<a class="side-menu__item" href="javascript:;">
{{childrenSubItem1.title}}
</a>
}
</li>
}
</ul>
}
</li>
}
</ul>
}
</li>
}
</ul>
}
</li>
@if (menuItem.type === 'external') {
<a class="side-menu__item" target="_blank" [routerLink]="!menuItem.type ? null : [menuItem.path]"
routerLinkActive="active" (click)="setNavActive($event, menuItem.path ?? '')">
@if (menuItem.icon) {
<span [appSvgReplace]="menuItem.icon" class="iconclick"></span>
}
<span class="side-menu__label">{{ menuItem.title }}
@if(menuItem.badgeValue){
<span
class="badge bg-{{ menuItem.badgeClass }} text-{{menuItem.badgeText}} float-end">{{menuItem.badgeValue
}}</span>
}
</span>
</a>
}
}
<!-- End::slide -->
</ul>
<div class="slide-right" id="slide-right" (click)="rightArrowFn()">
<svg xmlns="http://www.w3.org/2000/svg" fill="#7b8191" width="24" height="24" viewBox="0 0 24 24">
<path d="M10.707 17.707 16.414 12l-5.707-5.707-1.414 1.414L13.586 12l-4.293 4.293z"></path>
</svg>
</div>
</nav>
</ngx-simplebar>
</div>
</aside>
@@ -1,526 +0,0 @@
import { Component, Renderer2, HostListener, ElementRef, } from '@angular/core';
import { Menu, NavService } from '../../services/nav.service';
import { Subscription, fromEvent } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';
import { NavigationEnd, Router, RouterLink, RouterLinkActive } from '@angular/router';
import { AppStateService } from '../../services/app-state.service';
import { NgClass, NgStyle } from '@angular/common';
import { SimplebarAngularModule } from 'simplebar-angular';
import { SvgReplaceDirective } from '../../directives/svgReplace.directive';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.html',
styleUrl: './sidebar.scss',
imports: [NgClass, RouterLink, SimplebarAngularModule, NgStyle, RouterLinkActive, SvgReplaceDirective]
})
export class Sidebar {
doublemenuTooltiPosition = 'right'
eventTriggered: boolean = false;
screenWidth!: number;
public localdata = localStorage;
public windowSubscribe$!: Subscription;
options = { autoHide: false, scrollbarMinSize: 100 };
public menuItems!: Menu[];
public menuitemsSubscribe$!: Subscription;
constructor(
private navServices: NavService,
public router: Router,
public renderer: Renderer2,
private sanitizer: DomSanitizer,
private appStateService: AppStateService,
private elementRef: ElementRef
) { }
isDoubleMenu(): boolean {
const htmlElement = document.querySelector('[data-vertical-style="doublemenu"]');
return htmlElement !== null;
}
// Method to determine if tooltip should be shown
shouldShowTooltip(menuItem: any): boolean {
return this.isDoubleMenu() && menuItem.title !== '';
}
clearNavDropdown() {
this.menuItems?.forEach((a: any) => {
a.active = false;
a?.children?.forEach((b: any) => {
b.active = false;
b?.children?.forEach((c: any) => {
c.active = false;
});
});
});
}
ngOnInit() {
let bodyElement: any = document.querySelector('.main-content');
bodyElement.onclick = () => {
if (localStorage.getItem('layoutStyles') == 'icontext' || localStorage.getItem('layoutStyles') == 'icon-hover') {
document.querySelector('html')?.removeAttribute('data-icon-text')
}
};
this.menuitemsSubscribe$ = this.navServices.items.subscribe((items) => {
this.menuItems = items;
});
this.setNavActive(null, this.router.url);
this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
this.setNavActive(null, this.router.url);
}
});
const WindowResize = fromEvent(window, 'resize');
// subscribing the Observable
if (WindowResize) {
this.windowSubscribe$ = WindowResize.subscribe(() => {
// to check and adjst the menu on screen size change
// checkHoriMenu();
});
}
if (document.querySelector('html')?.getAttribute('data-nav-layout') == 'horizontal' && window.innerWidth >= 992) { this.clearNavDropdown(); }
}
// Start of Set menu Active event
setNavActive(event: any, currentPath: string, menuData = this.menuItems) {
if (event) {
if (event?.ctrlKey) {
return;
}
}
let html = this.elementRef.nativeElement.ownerDocument.documentElement;
//if (html.getAttribute('data-nav-style') != "icon-hover" && html.getAttribute('data-nav-style') != "menu-hover") {
// if (!event?.ctrlKey) {
for (const item of menuData) {
if (item.path === currentPath) {
item.active = true;
item.selected = true;
this.setMenuAncestorsActive(item);
} else if (!item.active && !item.selected) {
item.active = false; // Set active to false for items not matching the target
item.selected = false; // Set active to false for items not matching the target
} else {
this.removeActiveOtherMenus(item);
}
if (item.children && item.children.length > 0) {
this.setNavActive(event, currentPath, item.children);
}
}
// }
//}
if (window.innerWidth <= 996) {
html?.setAttribute('data-toggled', html?.getAttribute('data-toggled') == 'close' ? 'close' : 'close');
}
if (html?.getAttribute('data-vertical-style') == "icontext" && html?.getAttribute('data-icon-text') == 'open' && window.innerWidth >= 992) { html?.setAttribute('data-icon-text', 'close') }
}
getParentObject(obj: any, childObject: Menu) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object' && JSON.stringify(obj[key]) === JSON.stringify(childObject)) {
return obj; // Return the parent object
}
if (typeof obj[key] === 'object') {
const parentObject: any = this.getParentObject(obj[key], childObject);
if (parentObject !== null) {
return parentObject;
}
}
}
}
return null; // Object not found
}
hasParent = false;
hasParentLevel = 0;
setMenuAncestorsActive(targetObject: Menu) {
const parent = this.getParentObject(this.menuItems, targetObject);
let html = document.documentElement;
if (parent) {
if (this.hasParentLevel >= 2) {
this.hasParent = true;
}
parent.active = true;
parent.selected = true;
this.hasParentLevel += 1;
this.setMenuAncestorsActive(parent);
}
else if (!this.hasParent) {
this.hasParentLevel = 0;
if (html.getAttribute('data-vertical-style') == 'doublemenu') {
if (window.innerWidth < 992) {
html.setAttribute('data-toggled', 'close');
} else {
html.setAttribute('data-toggled', 'double-menu-close');
}
}
} else {
this.hasParentLevel = 0;
this.hasParent = false;
}
}
removeActiveOtherMenus(item: any) {
if (item) {
if (Array.isArray(item)) {
for (const val of item) {
val.active = false;
val.selected = false;
}
}
item.active = false;
item.selected = false;
if (item.children && item.children.length > 0) {
this.removeActiveOtherMenus(item.children);
}
}
else {
return;
}
}
// Start of Toggle menu event
toggleNavActive(event: any, targetObject: Menu, menuData = this.menuItems, state?: any) {
let html = document.documentElement;
let element = event.target;
if (html.getAttribute('data-nav-style') != "icon-hover" && html.getAttribute('data-nav-style') != "menu-hover" || (window.innerWidth < 992) || (html.getAttribute('data-nav-layout') != "horizontal") && (html.getAttribute('data-nav-style') != "icon-hover-closed" && html.getAttribute('data-nav-style') != "menu-hover-closed")) {
for (const item of menuData) {
if (item === targetObject) {
if (html.getAttribute('data-vertical-style') == 'doublemenu' && item.active && window.innerWidth > 992 && state) { return }
item.active = !item.active;
if (item.active) {
this.closeOtherMenus(menuData, item);
}
this.setAncestorsActive(menuData, item);
} else if (!item.active) {
if (html.getAttribute('data-vertical-style') != 'doublemenu') {
item.active = false; // Set active to false for items not matching the target
}
}
if (item.children && item.children.length > 0) {
this.toggleNavActive(event, targetObject, item.children);
}
}
if (targetObject?.children && targetObject.active) {
if (html.getAttribute('data-vertical-style') == 'doublemenu' && html.getAttribute('data-toggled') != 'double-menu-open') {
html.setAttribute('data-toggled', 'double-menu-open');
}
}
if (element && html.getAttribute("data-nav-layout") == 'horizontal' && (html.getAttribute("data-nav-style") == 'menu-click' || html.getAttribute("data-nav-style") == 'icon-click')) {
const listItem = element.closest("li");
if (listItem) {
// Find the first sibling <ul> element
const siblingUL = listItem.querySelector("ul");
let outterUlWidth = 0;
let listItemUL = listItem.closest('ul:not(.main-menu)');
while (listItemUL) {
listItemUL = listItemUL.parentElement.closest('ul:not(.main-menu)');
if (listItemUL) {
outterUlWidth += listItemUL.clientWidth;
}
}
if (siblingUL) {
// You've found the sibling <ul> element
let siblingULRect = listItem.getBoundingClientRect();
if (html.getAttribute('dir') == 'rtl') {
if ((siblingULRect.left - siblingULRect.width - outterUlWidth + 150 < 0 && outterUlWidth < window.innerWidth) && (outterUlWidth + siblingULRect.width + siblingULRect.width < window.innerWidth)) {
targetObject.dirchange = true;
} else {
targetObject.dirchange = false;
}
} else {
if ((outterUlWidth + siblingULRect.right + siblingULRect.width + 50 > window.innerWidth && siblingULRect.right >= 0) && (outterUlWidth + siblingULRect.width + siblingULRect.width < window.innerWidth)) {
targetObject.dirchange = true;
} else {
targetObject.dirchange = false;
}
}
}
setTimeout(() => {
let computedValue = siblingUL.getBoundingClientRect();
if ((computedValue.bottom) > window.innerHeight) {
siblingUL.style.height = (window.innerHeight - computedValue.top - 8) + 'px !important';
siblingUL.style.overflow = 'auto !important';
}
}, 100);
}
}
}
else {
for (const item of menuData) {
if (item === targetObject) {
if (html.getAttribute('data-vertical-style') == 'doublemenu' && item.active && window.innerWidth > 992 && state) { return }
item.active = !item.active;
if (item.active) {
this.closeOtherMenus(menuData, item);
}
this.setAncestorsActive(menuData, item);
}
}
}
if (html.getAttribute('data-vertical-style') == 'icontext') {
document.querySelector('html')?.setAttribute('data-icon-text', 'open')
} else {
document.querySelector('html')?.removeAttribute('data-icon-text')
}
}
setAncestorsActive(menuData: Menu[], targetObject: Menu) {
let html = document.documentElement;
const parent = this.findParent(menuData, targetObject);
if (parent) {
parent.active = true;
if (parent.active) {
html.setAttribute('data-toggled', 'double-menu-open');
}
this.setAncestorsActive(menuData, parent);
}
}
closeOtherMenus(menuData: Menu[], targetObject: Menu) {
for (const item of menuData) {
if (item !== targetObject) {
item.active = false;
if (item.children && item.children.length > 0) {
this.closeOtherMenus(item.children, targetObject);
}
}
}
}
findParent(menuData: Menu[], targetObject: Menu) {
for (const item of menuData) {
if (item.children && item.children.includes(targetObject)) {
return item;
}
if (item.children && item.children.length > 0) {
const parent: any = this.findParent(item.children, targetObject);
if (parent) {
return parent;
}
}
}
return null;
}
// End of Toggle menu event
HoverToggleInnerMenuFn(event: Event, item: Menu) {
let html = document.documentElement;
let element = event.target as HTMLElement;
if (element && html.getAttribute("data-nav-layout") == 'horizontal' && (html.getAttribute("data-nav-style") == 'menu-hover' || html.getAttribute("data-nav-style") == 'icon-hover')) {
const listItem = element.closest("li");
if (listItem) {
// Find the first sibling <ul> element
const siblingUL = listItem.querySelector("ul");
let outterUlWidth = 0;
let listItemUL: any = listItem.closest('ul:not(.main-menu)');
while (listItemUL) {
listItemUL = listItemUL.parentElement?.closest('ul:not(.main-menu)');
if (listItemUL) {
outterUlWidth += listItemUL.clientWidth;
}
}
if (siblingUL) {
// You've found the sibling <ul> element
let siblingULRect = listItem.getBoundingClientRect();
if (html.getAttribute('dir') == 'rtl') {
if ((siblingULRect.left - siblingULRect.width - outterUlWidth + 150 < 0 && outterUlWidth < window.innerWidth) && (outterUlWidth + siblingULRect.width + siblingULRect.width < window.innerWidth)) {
item.dirchange = true;
} else {
item.dirchange = false;
}
} else {
if ((outterUlWidth + siblingULRect.right + siblingULRect.width + 50 > window.innerWidth && siblingULRect.right >= 0) && (outterUlWidth + siblingULRect.width + siblingULRect.width < window.innerWidth)) {
item.dirchange = true;
} else {
item.dirchange = false;
}
}
}
}
}
}
ngAfterViewInit(): void {
//Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.
//Add 'implements AfterViewInit' to the class.
// checkHoriMenu();
}
ngOnDestroy() {
this.menuitemsSubscribe$.unsubscribe();
this.windowSubscribe$.unsubscribe();
document.querySelector('html')?.setAttribute('data-vertical-style', 'overlay');
document.querySelector('html')?.setAttribute('data-nav-layout', 'vertical');
}
leftArrowFn() {
// Used to move the slide of the menu in Horizontal and also remove the arrows after click if there was no space
// Used to Slide the menu to Left side
let slideLeft = document.querySelector('.slide-left') as HTMLElement;
let slideRight = document.querySelector('.slide-right') as HTMLElement;
let menuNav = document.querySelector('.main-menu') as HTMLElement;
let mainContainer1 = document.querySelector('.main-sidebar') as HTMLElement;
let marginRightValue = Math.ceil(Number(window.getComputedStyle(menuNav).marginInlineStart.split('px')[0]));
let mainContainer1Width = mainContainer1.offsetWidth;
if (menuNav.scrollWidth > mainContainer1.offsetWidth) {
if (marginRightValue < 0 && !(Math.abs(marginRightValue) < mainContainer1Width)) {
menuNav.style.marginInlineStart = Number(menuNav.style.marginInlineStart.split('px')[0]) + Math.abs(mainContainer1Width) + 'px';
slideRight.classList.remove('d-none');
} else if (marginRightValue >= 0) {
menuNav.style.marginInlineStart = '0px';
slideLeft.classList.add('d-none');
slideRight.classList.remove('d-none');
} else {
menuNav.style.marginInlineStart = '0px';
slideLeft.classList.add('d-none');
slideRight.classList.remove('d-none');
}
}
else {
menuNav.style.marginInlineStart = "0px";
slideLeft.classList.add('d-none');
}
let element = document.querySelector(".main-menu > .slide.open") as HTMLElement;
let element1 = document.querySelector(".main-menu > .slide.open >ul") as HTMLElement;
if (element) {
element.classList.remove("open")
}
if (element1) {
element1.style.display = "none"
}
}
rightArrowFn() {
// Used to move the slide of the menu in Horizontal and also remove the arrows after click if there was no space
// Used to Slide the menu to Right side
let slideLeft = document.querySelector('.slide-left') as HTMLElement;
let slideRight = document.querySelector('.slide-right') as HTMLElement;
let menuNav = document.querySelector('.main-menu') as HTMLElement;
let mainContainer1 = document.querySelector('.main-sidebar') as HTMLElement;
let marginRightValue = Math.ceil(Number(window.getComputedStyle(menuNav).marginInlineStart.split('px')[0]));
let check = menuNav.scrollWidth - mainContainer1.offsetWidth;
let mainContainer1Width = mainContainer1.offsetWidth;
if (menuNav.scrollWidth > mainContainer1.offsetWidth) {
if (Math.abs(check) > Math.abs(marginRightValue)) {
if (!(Math.abs(check) > Math.abs(marginRightValue) + mainContainer1Width)) {
mainContainer1Width = Math.abs(check) - Math.abs(marginRightValue);
slideRight.classList.add('d-none');
}
menuNav.style.marginInlineStart = Number(menuNav.style.marginInlineStart.split('px')[0]) - Math.abs(mainContainer1Width) + 'px';
slideLeft.classList.remove('d-none');
}
}
let element = document.querySelector(".main-menu > .slide.open") as HTMLElement
let element1 = document.querySelector(".main-menu > .slide.open >ul") as HTMLElement
if (element) {
element.classList.remove("open")
}
if (element1) {
element1.style.display = "none"
}
}
// Addding sticky-pin
scrolled = false;
@HostListener('window:scroll', [])
onWindowScroll() {
this.scrolled = window.scrollY > 10;
const sections = document.querySelectorAll('.side-menu__item');
const scrollPos =
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop;
sections.forEach((ele, i) => {
const currLink = sections[i];
const val: any = currLink.getAttribute('value');
const refElement: any = document.querySelector('#' + val);
// Add a null check here before accessing properties of refElement
if (refElement !== null) {
const scrollTopMinus = scrollPos + 73;
if (
refElement.offsetTop <= scrollTopMinus &&
refElement.offsetTop + refElement.offsetHeight > scrollTopMinus
) {
document.querySelector('.nav-scroll')?.classList.remove('active');
currLink.classList.add('active');
} else {
currLink.classList.remove('active');
}
}
});
}
@HostListener('window:resize', ['$event'])
onResize(event: any): void {
this.menuResizeFn();
this.screenWidth = window.innerWidth;
// Check if the event hasn't been triggered and the screen width is less than or equal to your breakpoint
if (!this.eventTriggered && this.screenWidth <= 992) {
document.documentElement?.setAttribute('data-toggled', 'close')
// Trigger your event or perform any action here
this.eventTriggered = true; // Set the flag to true to prevent further triggering
} else if (this.screenWidth > 992) {
// Reset the flag when the screen width goes beyond the breakpoint
this.eventTriggered = false;
}
}
WindowPreSize: number[] = [window.innerWidth];
menuResizeFn(): void {
this.WindowPreSize.push(window.innerWidth);
const html = document.documentElement;
if (this.WindowPreSize.length > 2) {
this.WindowPreSize.shift();
}
if (this.WindowPreSize.length > 1) {
if (this.WindowPreSize[this.WindowPreSize.length - 1] < 996 && this.WindowPreSize[this.WindowPreSize.length - 2] >= 996) {
// less than 996
html.setAttribute('data-toggled', 'close');
}
if (this.WindowPreSize[this.WindowPreSize.length - 1] >= 996 && this.WindowPreSize[this.WindowPreSize.length - 2] < 996) {
// greater than 996
html.removeAttribute('data-toggled');
document.querySelector('#responsive-overlay')?.classList.remove('active');
}
}
if ((this.WindowPreSize[this.WindowPreSize.length - 1] >= 996) && (this.WindowPreSize[this.WindowPreSize.length - 2] < 996)) {
if (html.getAttribute('data-vertical-style') === 'doublemenu') {
const doublemenuactive = document.querySelectorAll(".double-menu-active .active");
if (doublemenuactive.length > 0) {
html.setAttribute('data-toggled', 'double-menu-open');
} else {
html.setAttribute('data-toggled', 'double-menu-close');
}
} else {
html.setAttribute('data-toggled', '');
}
}
}
}
@@ -1,543 +0,0 @@
<div id="hs-overlay-switcher"
class="hs-overlay hidden ti-offcanvas border-defaultborder dark:border-defaultborder/10 ti-offcanvas-right hs-overlay-backdrop-open:bg-[#32325180] dark:hs-overlay-backdrop-open:bg-[#323251cc]"
data-hs-overlay-backdrop="false">
<div
class="ti-offcanvas-header border-defaultborder dark:border-defaultborder/10 z-10 relative items-center !block !p-0">
<div class="flex items-center justify-between p-3">
<h5 class="ti-offcanvas-title">
Switcher
</h5>
<button type="button"
class="ti-btn flex-shrink-0 p-0 transition-none text-defaulttextcolor dark:text-defaulttextcolor/70 hover:text-gray-700 focus:ring-gray-400 focus:ring-offset-white dark:hover:text-white/80 dark:focus:ring-white/10 dark:focus:ring-offset-white/10 !m-0"
data-hs-overlay="#hs-overlay-switcher">
<span class="sr-only">Close modal</span>
<i class="ri-close-circle-line leading-none items-center text-lg"></i>
</button>
</div>
<nav class="flex border-t border-defaultborder dark:border-defaultborder/10 " aria-label="Tabs" role="tablist">
<button type="button"
class="hs-tab-active:bg-success/20 w-full !py-2 !px-4 hs-tab-active:border-b-transparent text-defaultsize border-0 hs-tab-active:!text-success dark:hs-tab-active:!bg-success/20 dark:hs-tab-active:border-b-white/10 dark:hs-tab-active:!text-success -mb-px font-semibold text-center text-defaulttextcolor dark:text-defaulttextcolor/70 rounded-none dark:bg-bodybg dark:border-white/10 cursor-pointer active"
id="switcher-item-1" data-hs-tab="#switcher-1" aria-controls="switcher-1" role="tab">
Theme Style
</button>
<button type="button"
class="hs-tab-active:bg-success/20 w-full !py-2 !px-4 hs-tab-active:border-b-transparent text-defaultsize border-0 hs-tab-active:!text-success dark:hs-tab-active:!bg-success/20 dark:hs-tab-active:border-b-white/10 dark:hs-tab-active:!text-success -mb-px font-semibold text-center text-defaulttextcolor dark:text-defaulttextcolor/70 rounded-none dark:bg-bodybg dark:border-white/10 cursor-pointer"
id="switcher-item-2" data-hs-tab="#switcher-2" aria-controls="switcher-2" role="tab">
Theme Colors
</button>
</nav>
</div>
<div class="ti-offcanvas-body overflow-auto border-defaultborder dark:border-defaultborder/10 relative "
id="switcher-body">
<div id="switcher-1" role="tabpanel" aria-labelledby="switcher-item-1" class="">
<div class="">
<p class="switcher-style-head">Theme Color Mode:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="theme-style" class="ti-form-radio form-check-input" id="switcher-light-theme"
(click)="updateTheme('light')" [checked]="localdata['theme'] == 'light'">
<label for="switcher-light-theme"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Light</label>
</div>
<div class="flex items-center">
<input type="radio" name="theme-style" class="ti-form-radio form-check-input" id="switcher-dark-theme"
(click)="updateTheme('dark')" [checked]="localdata['theme'] == 'dark'">
<label for="switcher-dark-theme"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Dark</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Directions:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="direction" class="ti-form-radio form-check-input" id="switcher-ltr"
(click)="updateDirection('ltr')" [checked]="localdata['direction'] != 'rtl'">
<label for="switcher-ltr"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">LTR</label>
</div>
<div class="flex items-center">
<input type="radio" name="direction" class="ti-form-radio form-check-input" id="switcher-rtl"
(click)="updateDirection('rtl')" [checked]="localdata['direction'] == 'rtl'">
<label for="switcher-rtl"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">RTL</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Navigation Styles:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="navigation-style" class="ti-form-radio form-check-input" id="switcher-vertical"
[checked]="localdata['navigationStyles'] == 'vertical'" (click)="updatemenuType('vertical')">
<label for="switcher-vertical"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Vertical</label>
</div>
<div class="flex items-center">
<input type="radio" name="navigation-style" class="ti-form-radio form-check-input" id="switcher-horizontal"
[checked]="localdata['navigationStyles'] == 'horizontal'" (click)="updatemenuType('horizontal')">
<label for="switcher-horizontal"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Horizontal</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Navigation Menu Style:</p>
<div class="grid grid-cols-2 gap-2 switcher-style">
<div class="flex items-center">
<input type="radio" name="navigation-data-menu-styles" class="ti-form-radio form-check-input"
id="switcher-menu-click" (click)="updatemenuStyle('menu-click')"
[checked]="localdata['menuStyles'] == 'menu-click'">
<label for="switcher-menu-click"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Menu
Click</label>
</div>
<div class="flex items-center">
<input type="radio" name="navigation-data-menu-styles" class="ti-form-radio form-check-input"
id="switcher-menu-hover" (click)="updatemenuStyle('menu-hover')"
[checked]="localdata['menuStyles'] == 'menu-hover'">
<label for="switcher-menu-hover"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Menu
Hover</label>
</div>
<div class="flex items-center">
<input type="radio" name="navigation-data-menu-styles" class="ti-form-radio form-check-input"
id="switcher-icon-click" (click)="updatemenuStyle('icon-click')"
[checked]="localdata['menuStyles'] == 'icon-click'">
<label for="switcher-icon-click"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Icon
Click</label>
</div>
<div class="flex items-center">
<input type="radio" name="navigation-data-menu-styles" class="ti-form-radio form-check-input"
id="switcher-icon-hover" (click)="updatemenuStyle('icon-hover')"
[checked]="localdata['menuStyles'] == 'icon-hover'">
<label for="switcher-icon-hover"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Icon
Hover</label>
</div>
</div>
<div class="px-4 text-secondary text-xs"><b class="me-2">Note:</b>Works same for both Vertical and
Horizontal
</div>
</div>
<div class=" sidemenu-layout-styles">
<p class="switcher-style-head">Sidemenu Layout Syles:</p>
<div class="grid grid-cols-2 gap-2 switcher-style">
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-default-menu" (click)="updatelayoutStyles('default')"
[checked]="localdata['layoutStyles'] == 'default'">
<label for="switcher-default-menu"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold ">Default
Menu</label>
</div>
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-closed-menu" (click)="updatelayoutStyles('closed')"
[checked]="localdata['layoutStyles'] == 'closed'">
<label for="switcher-closed-menu"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold ">
Closed
Menu</label>
</div>
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-icontext-menu" (click)="updatelayoutStyles('icontext')"
[checked]="localdata['layoutStyles'] == 'icontext'">
<label for="switcher-icontext-menu"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold ">Icon
Text</label>
</div>
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-icon-overlay" (click)="updatelayoutStyles('overlay')"
[checked]="localdata['layoutStyles'] == 'overlay'">
<label for="switcher-icon-overlay"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold ">Icon
Overlay</label>
</div>
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-detached" (click)="updatelayoutStyles('detached')"
[checked]="localdata['layoutStyles'] == 'detached'">
<label for="switcher-detached"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold ">Detached</label>
</div>
<div class="flex items-center">
<input type="radio" name="sidemenu-layout-styles" class="ti-form-radio form-check-input"
id="switcher-double-menu" (click)="updatelayoutStyles('doublemenu')"
[checked]="localdata['layoutStyles'] == 'doublemenu'">
<label for="switcher-double-menu"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Double
Menu</label>
</div>
</div>
<div class="px-4 text-secondary text-xs"><b class="me-2">Note:</b>Navigation menu styles won't work
here.</div>
</div>
<div>
<p class="switcher-style-head">Page Styles:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="data-page-styles" class="ti-form-radio form-check-input" id="switcher-regular"
(click)="updatepageStyles('regular')" [checked]="localdata['pageStyles'] != 'classic'">
<label for="switcher-regular"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Regular</label>
</div>
<div class="flex items-center">
<input type="radio" name="data-page-styles" class="ti-form-radio form-check-input" id="switcher-classic"
(click)="updatepageStyles('classic')" [checked]="localdata['pageStyles'] == 'classic'">
<label for="switcher-classic"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Classic</label>
</div>
<div class="flex items-center">
<input type="radio" name="data-page-styles" class="ti-form-radio form-check-input" id="switcher-modern"
(click)="updatepageStyles('modern')" [checked]="localdata['pageStyles'] == 'modern'">
<label for="switcher-modern"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">
Modern</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Layout Width Styles:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="layout-width" class="ti-form-radio form-check-input" id="switcher-full-width"
(click)="updatewidthStyles('full-width')" [checked]="localdata['widthStyles'] != 'full-width'">
<label for="switcher-full-width"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">FullWidth</label>
</div>
<div class="flex items-center">
<input type="radio" name="layout-width" class="ti-form-radio form-check-input" id="switcher-boxed"
(click)="updatewidthStyles('boxed')" [checked]="localdata['widthStyles'] == 'boxed'">
<label for="switcher-boxed"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Boxed</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Menu Positions:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="data-menu-positions" class="ti-form-radio form-check-input"
id="switcher-menu-fixed" (click)="updatemenuPosition('fixed')"
[checked]="localdata['menuPosition'] != 'scrollable'">
<label for="switcher-menu-fixed"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Fixed</label>
</div>
<div class="flex items-center">
<input type="radio" name="data-menu-positions" class="ti-form-radio form-check-input"
id="switcher-menu-scroll" (click)="updatemenuPosition('scrollable')"
[checked]="localdata['menuPosition'] == 'scrollable'">
<label for="switcher-menu-scroll"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Scrollable
</label>
</div>
</div>
</div>
<div>
<p class="switcher-style-head">Header Positions:</p>
<div class="grid grid-cols-3 switcher-style">
<div class="flex items-center">
<input type="radio" name="data-header-positions" class="ti-form-radio form-check-input"
id="switcher-header-fixed" (click)="updateheaderPosition('fixed')"
[checked]="localdata['headerPosition'] != 'scrollable'">
<label for="switcher-header-fixed"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">
Fixed</label>
</div>
<div class="flex items-center">
<input type="radio" name="data-header-positions" class="ti-form-radio form-check-input"
id="switcher-header-scroll" (click)="updateheaderPosition('scrollable')"
[checked]="localdata['headerPosition'] == 'scrollable'">
<label for="switcher-header-scroll"
class="text-defaultsize text-defaulttextcolor dark:text-defaulttextcolor/70 ms-2 font-semibold">Scrollable
</label>
</div>
</div>
</div>
</div>
<div id="switcher-2" class="hidden" role="tabpanel" aria-labelledby="switcher-item-2">
<div class="theme-colors">
<p class="switcher-style-head">Menu Colors:</p>
<div class="flex switcher-style space-x-3 ">
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-white" type="radio"
name="menu-colors" id="switcher-menu-light" checked (click)="updatemenuColor('light')"
[checked]="localdata['menuColor'] == 'light'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Light Menu
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-dark" type="radio"
name="menu-colors" id="switcher-menu-dark" (click)="updatemenuColor('dark')"
[checked]="localdata['menuColor'] == 'dark'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Dark Menu
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-primary" type="radio"
name="menu-colors" id="switcher-menu-primary" (click)="updatemenuColor('color')"
[checked]="localdata['menuColor'] == 'color'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Color Menu
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-gradient" type="radio"
name="menu-colors" id="switcher-menu-gradient" (click)="updatemenuColor('gradient')"
[checked]="localdata['menuColor'] == 'gradient'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Gradient Menu
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-transparent" type="radio"
name="menu-colors" id="switcher-menu-transparent" (click)="updatemenuColor('transparent')"
[checked]="localdata['menuColor'] == 'transparent'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Transparent Menu
</span>
</div>
</div>
<div class="px-4 text-[#8c9097] dark:text-white/50 text-[.6875rem]"><b class="me-2">Note:</b>If you want to
change color Menu
dynamically
change from below Theme Primary color picker.</div>
</div>
<div class="theme-colors">
<p class="switcher-style-head">Header Colors:</p>
<div class="flex switcher-style space-x-3 ">
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-white !border"
type="radio" name="header-colors" id="switcher-header-light" checked (click)="updateheaderColor('light')"
[checked]="localdata['headerColor'] == 'light'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Light Header
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-dark" type="radio"
name="header-colors" id="switcher-header-dark" (click)="updateheaderColor('dark')"
[checked]="localdata['headerColor'] == 'dark'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Dark Header
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-primary" type="radio"
name="header-colors" id="switcher-header-primary" (click)="updateheaderColor('color')"
[checked]="localdata['headerColor'] == 'color'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Color Header
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-gradient" type="radio"
name="header-colors" id="switcher-header-gradient" (click)="updateheaderColor('gradient')"
[checked]="localdata['headerColor'] == 'gradient'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Gradient Header
</span>
</div>
<div class="hs-tooltip ti-main-tooltip ti-form-radio switch-select ">
<input class="hs-tooltip-toggle ti-form-radio form-check-input color-input color-transparent"
type="radio" name="header-colors" id="switcher-header-transparent"
(click)="updateheaderColor('transparent')" [checked]="localdata['headerColor'] == 'transparent'">
<span
class="hs-tooltip-content ti-main-tooltip-content !py-1 !px-2 !bg-black text-xs font-medium !text-white shadow-sm dark:!bg-black"
role="tooltip">
Transparent Header
</span>
</div>
</div>
<div class="px-4 text-[#8c9097] dark:text-white/50 text-[.6875rem]"><b class="me-2">Note:</b>If you want to
change color
Header dynamically
change from below Theme Primary color picker.</div>
</div>
<div class="theme-colors">
<p class="switcher-style-head">Theme Primary:</p>
<div class="flex switcher-style space-x-3 ">
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-primary-1" type="radio" name="theme-primary"
id="switcher-primary" (click)="updateprimary('58,88,146')"
[checked]="localdata['themePrimary'] == 'rgb(58,88,146)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-primary-2" type="radio" name="theme-primary"
id="switcher-primary1" (click)="updateprimary('92,144,163')"
[checked]="localdata['themePrimary'] == 'rgb(92,144,163)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-primary-3" type="radio" name="theme-primary"
id="switcher-primary2" (click)="updateprimary('161,90,223')"
[checked]="localdata['themePrimary'] == 'rgb(161,90,223)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-primary-4" type="radio" name="theme-primary"
id="switcher-primary3" (click)="updateprimary('78,172,76')"
[checked]="localdata['themePrimary'] == 'rgb(78,172,76)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-primary-5" type="radio" name="theme-primary"
id="switcher-primary4" (click)="updateprimary('223,90,90')"
[checked]="localdata['themePrimary'] == 'rgb(223,90,90)'">
</div>
<div class="ti-form-radio switch-select color-primary-light">
<div class="theme-container-primary"></div>
<div class="pickr-container-primary">
<div class="pickr">
<button type="button" type="button" role="button" aria-label="toggle color picker dialog"
class="color-bg-transparent pcr-button" style="--pcr-color: rgba(132, 90, 223, 1);"
[style.background]="defaultPrimary" [cpAlphaChannel]="'disabled'" [cpOutputFormat]="'rgba'"
[(colorPicker)]="defaultPrimary" (cpSliderDragEnd)="dynamicLightPrimary($event)"></button>
</div>
</div>
</div>
</div>
</div>
<div class="theme-colors">
<p class="switcher-style-head">Theme Background:</p>
<div class="flex switcher-style space-x-3 ">
<div class="ti-form-radio switch-select">
<input class="ti-form-radio form-check-input color-input color-bg-1" type="radio" name="theme-background"
id="switcher-background" (click)="
updateBackground({
main: '20,30,96',
secondary: '25, 38, 101',
accent: '25, 38, 101',
overlay: 'rgba(255,255,255,0.1)',
primary:'rgba(255,255,255,0.1)',
theme: 'dark'
})
" [checked]="localdata['themeBackground']?.['main'] === 'rgb(20,30,96)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio color-input form-check-input color-bg-2" type="radio" name="theme-background"
id="switcher-background1" (click)="
updateBackground({
main: '8,78,115',
secondary: ' 13, 86, 120',
accent: '13, 86, 120',
overlay: 'rgba(255,255,255,0.1)',
primary:'rgba(255,255,255,0.1)',
theme: 'dark'
})
" [checked]="localdata['themeBackground']?.['main'] === 'rgb(8,78,115)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio color-input form-check-input color-bg-3" type="radio" name="theme-background"
id="switcher-background2" (click)="
updateBackground({
main: '90,37,135',
secondary: '95, 45, 140',
accent: '95, 45, 140',
overlay: 'rgb(95, 45, 140)',
primary:'rgb(95, 45, 140)',
theme: 'dark'
})
" [checked]="localdata['themeBackground']?.['main'] === 'rgb(90,37,135)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio color-input form-check-input color-bg-4" type="radio" name="theme-background"
id="switcher-background3" (click)="
updateBackground({
main: '24,101,51',
secondary: '29, 109, 56',
accent: '29, 109, 56',
overlay: 'rgba(255,255,255,0.1)',
primary:'rgba(255,255,255,0.1)',
theme: 'dark'
})
" [checked]="localdata['themeBackground']?.['main'] === 'rgb(24,101,51)'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio color-input form-check-input color-bg-5" type="radio" name="theme-background"
id="switcher-background4" (click)="
updateBackground({ main: '120, 66, 20', secondary: '125, 74, 25', accent: '125, 74, 25', overlay: 'rgba(255,255,255,0.1)', primary:'rgba(255,255,255,0.1)', theme: 'dark' })
" [checked]="localdata['themeBackground']?.['main'] === 'rgb(120,66,20)'">
</div>
<div class="ti-form-radio switch-select color-bg-transparent">
<div class="theme-container-background hidden"></div>
<div class="pickr-container-background">
<div class="pickr">
<button type="button" role="button" aria-label="toggle color picker dialog"
class="color-bg-transparent pcr-button" style="--pcr-color: rgba(132, 90, 223, 1);"
[style.background]="defaultBg" [cpAlphaChannel]="'disabled'" [cpOutputFormat]="'rgba'"
[(colorPicker)]="defaultBg" (cpSliderDragEnd)="dynamicTranparentBgPrimary($event)"></button>
</div>
</div>
</div>
</div>
</div>
<div class="menu-image theme-colors">
<p class="switcher-style-head">Menu With Background Image:</p>
<div class="flex switcher-style space-x-3 flex-wrap gap-3">
<div class="ti-form-radio switch-select">
<input class="ti-form-radio bgimage-input form-check-input bg-img1" type="radio" name="theme-images"
id="switcher-bg-img" (click)="updateBgImage('bgimg1')"
[checked]="localdata['backgroundImage'] == 'bgimg1'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio bgimage-input form-check-input bg-img2" type="radio" name="theme-images"
id="switcher-bg-img1" (click)="updateBgImage('bgimg2')"
[checked]="localdata['backgroundImage'] == 'bgimg2'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio bgimage-input form-check-input bg-img3" type="radio" name="theme-images"
id="switcher-bg-img2" (click)="updateBgImage('bgimg3')"
[checked]="localdata['backgroundImage'] == 'bgimg3'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio bgimage-input form-check-input bg-img4" type="radio" name="theme-images"
id="switcher-bg-img3" (click)="updateBgImage('bgimg4')"
[checked]="localdata['backgroundImage'] == 'bgimg4'">
</div>
<div class="ti-form-radio switch-select">
<input class="ti-form-radio bgimage-input form-check-input bg-img5" type="radio" name="theme-images"
id="switcher-bg-img4" (click)="updateBgImage('bgimg5')"
[checked]="localdata['backgroundImage'] == 'bgimg5'">
</div>
</div>
</div>
</div>
</div>
<div class="ti-offcanvas-footer sm:flex justify-between">
<a href="javascript:void(0);" id="reset-all" class=" ti-btn ti-btn-danger-full m-1 w-full"
(click)="reset()">Reset</a>
</div>
</div>
<!-- ========== END Switcher ========== -->
@@ -1,191 +0,0 @@
import { Component, DOCUMENT, ElementRef, inject, Renderer2 } from '@angular/core';
import { AppStateService } from '../../services/app-state.service';
import { ColorPickerDirective } from 'ngx-color-picker';
@Component({
selector: 'app-switcher',
templateUrl: './switcher.html',
styleUrls: ['./switcher.scss'],
imports: [ColorPickerDirective]
})
export class Switcher {
public localdata: any;
private document = inject<Document>(DOCUMENT);
constructor(
private elementRef: ElementRef,
private appStateService: AppStateService,
private renderer: Renderer2
) {
this.appStateService.state$.subscribe(state => {
this.localdata = state;
});
}
updateTheme(theme: string) {
this.appStateService.updateState({ theme, menuColor: theme, headerColor: theme });
if (theme == 'light') {
this.appStateService.updateState({ theme, themeBackground: '', headerColor: 'light', menuColor: 'dark' });
let html = document.querySelector('html');
html?.style.removeProperty('--color-bodybg');
html?.style.removeProperty('--color-bodybg2');
html?.style.removeProperty('--color-light');
html?.style.removeProperty('--color-formcontrolbg');
html?.style.removeProperty('--color-inputborder');
html?.style.removeProperty('--color-gray3');
if (window.innerWidth <= 992) {
html?.setAttribute('data-toggled', 'close');
}
}
if (theme == 'dark') {
this.appStateService.updateState({ theme, themeBackground: '', headerColor: 'dark', menuColor: 'dark' });
let html = document.querySelector('html');
html?.style.removeProperty('--color-bodybg');
html?.style.removeProperty('--color-bodybg2');
html?.style.removeProperty('--color-light');
html?.style.removeProperty('--color-formcontrolbg');
html?.style.removeProperty('--color-inputborder');
html?.style.removeProperty('--color-gray3');
if (window.innerWidth <= 992) {
html?.setAttribute('data-toggled', 'close');
}
}
}
updateDirection(direction: string) {
let html = this.elementRef.nativeElement.ownerDocument.documentElement;
this.appStateService.updateState({ direction });
}
updatemenuType(navigationStyles: string) {
this.appStateService.updateState({ navigationStyles });
if (navigationStyles == 'horizontal') {
this.appStateService.updateState({ navigationStyles, menuStyles: 'menu-click', layoutStyles: '', });
const menuclickclosed = document.getElementById(
'switcher-menu-click'
) as HTMLInputElement;
menuclickclosed.checked = true;
setTimeout(() => {
const mainContentElement = document.querySelector(".main-content") as HTMLElement | null;
if (mainContentElement) {
mainContentElement.click();
}
}, 100);
} else if (navigationStyles == 'vertical') {
this.appStateService.updateState({ navigationStyles, menuStyles: '', layoutStyles: 'default', });
}
}
updatemenuStyle(menuStyles: string) {
this.appStateService.updateState({ menuStyles, layoutStyles: '' });
const navStyle = document.documentElement.getAttribute('data-nav-style');
if (navStyle === 'icon-hover') {
document.querySelector('.double-menu-active')?.setAttribute('style', 'display: none;');
const Sidebar: any = document.querySelector(".main-menu");
if (Sidebar) {
Sidebar.style.marginInline = "0px";
}
}
if (navStyle === 'icon-click') {
const Sidebar: any = document.querySelector(".main-menu");
if (Sidebar) {
Sidebar.style.marginInline = "0px";
}
}
}
updatelayoutStyles(layoutStyles: string) {
this.appStateService.updateState({ layoutStyles, menuStyles: '', navigationStyles: '' });
if (document.querySelector('html')?.getAttribute('data-vertical-style') == 'doublemenu') {
document.querySelector('.slide-menu')?.classList.add('double-menu-active');
}
else {
document.querySelector('.slide-menu')?.classList.remove('double-menu-active');
}
}
updatepageStyles(pageStyles: string) {
this.appStateService.updateState({ pageStyles });
}
updatewidthStyles(widthStyles: string) {
this.appStateService.updateState({ widthStyles });
}
updatemenuPosition(menuPosition: string) {
this.appStateService.updateState({ menuPosition });
}
updateheaderPosition(headerPosition: string) {
this.appStateService.updateState({ headerPosition });
}
updatemenuColor(menuColor: string) {
this.appStateService.updateState({ menuColor });
}
updateheaderColor(headerColor: string) {
this.appStateService.updateState({ headerColor: headerColor });
}
updateprimary(themePrimary: string) {
this.appStateService.updateState({ themePrimary: `rgb(${themePrimary})` });
}
updateBackground(themeBackground: any) {
const background = {
main: `rgb(${themeBackground.main})`,
secondary: `rgb(${themeBackground.secondary})`,
accent: `rgb(${themeBackground.accent})`,
overlay: themeBackground.overlay,
primary: themeBackground.primary,
theme: themeBackground.theme
}
this.appStateService.updateState({ themeBackground: background, menuColor: 'dark', headerColor: 'dark', theme: "dark" });
}
updateBgImage(backgroundImage: string) {
this.appStateService.updateState({ backgroundImage });
}
defaultPrimary = '#6c5ffc';
public dynamicLightPrimary(data: any): void {
this.defaultPrimary = data.color;
let primaryColor = this.convertRgbToIndividual(this.defaultPrimary)
this.updateprimary(primaryColor);
}
//background theme change
convertRgbToIndividual(value: string): string {
// Use a regular expression to extract the numeric values
const numericValues = value.match(/\d+/g) || [];
// Join the numeric values with spaces to get the desired format
return numericValues.join(' ');
}
public defaultBg = '#6c5ffc';
public dynamicTranparentBgPrimary(data: any): void {
this.defaultBg = data.color;
let bgRgb = this.convertRgbToIndividual(this.defaultBg);
let bgRgb2 = this.convertRgbToIndividual(this.defaultBg);
let bg1Update = bgRgb.split(' ').join(', ');
let bg2Update: any = bgRgb2.split(' ');
bg2Update[0] = Number(bg2Update[0]) + 14;
bg2Update[1] = Number(bg2Update[1]) + 14;
bg2Update[2] = Number(bg2Update[2]) + 14;
let bgColor = {
main: bg1Update, secondary: bg2Update.join(', '),
accent: bg2Update.join(', '), overlay: 'rgba(255,255,255,0.1)',
theme: 'dark',
}
this.updateBackground(bgColor);
}
reset() {
this.appStateService.applyReset();
}
}
@@ -1,6 +0,0 @@
<!-- Back To Top -->
<div class="scrollToTop" (click)="taptotop()"
[ngStyle]="{ display: show ? 'block' : 'none' }"
style="display: flex">
<span class="arrow"><i class="ri-arrow-up-s-fill text-xl leading-loose"></i></span>
</div>
@@ -1,35 +0,0 @@
import { ViewportScroller, NgStyle } from '@angular/common';
import { Component, HostListener, inject } from '@angular/core';
@Component({
selector: 'app-tab-to-top',
templateUrl: './tab-to-top.html',
styleUrl: './tab-to-top.scss',
imports: [NgStyle]
})
export class TabToTop {
private viewScroller = inject(ViewportScroller);
public show: boolean = false;
ngOnInit(): void {
}
@HostListener("window:scroll", [])
onWindowScroll() {
let number = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (number > 150) {
this.show = true;
} else {
this.show = false;
}
}
taptotop() {
let body: any = document.querySelector('body')
body.style.scrollBehavior = 'smooth';
}
}
@@ -1,2 +0,0 @@
<router-outlet />
@@ -1,14 +0,0 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-authentication-layout',
templateUrl: './authentication-layout.html',
styleUrl: './authentication-layout.scss',
imports: [RouterOutlet]
})
export class AuthenticationLayout {
}
@@ -1,46 +0,0 @@
<app-switcher />
<app-loader />
<div class="page">
<app-header />
<app-sidebar appHoverEffectSidebar />
<div class="content">
<div class="main-content" (click)="clickOnBody()">
<app-page-header />
<router-outlet />
</div>
</div>
<app-footer />
<app-tab-to-top />
</div>
<div #responsiveoverlay id="responsive-overlay" (click)="clearToggle()"></div>
@if (sessionTimeoutService.showWarning()) {
<div
class="hs-overlay ti-modal mt-[1.75rem] hs-overlay-backdrop-open:!bg-[#32325180] dark:hs-overlay-backdrop-open:!bg-[#323251cc] pointer-events-none !block">
<div class="ti-modal-box pointer-events-auto">
<div class="ti-modal-content !border !border-defaultborder dark:!border-defaultborder/10 !rounded-[0.5rem]">
<div class="ti-modal-body !p-[1.5rem]">
<div class="flex items-start gap-4">
<span class="!w-[3rem] !h-[3rem] !leading-[3rem] rounded-[50%] avatar bg-warning/10 !text-warning text-center">
<i class="fe fe-alert-triangle text-[1.125rem]"></i>
</span>
<div>
<p class="text-[1rem] font-semibold text-defaulttextcolor dark:text-defaulttextcolor/70 mb-2">Session Expiring Soon</p>
<p class="text-[#8c9097] dark:text-white/50 text-[0.875rem] mb-0">
You have been inactive. You will be signed out in {{ sessionTimeoutService.remainingSeconds() }} seconds unless you continue your session.
</p>
</div>
</div>
</div>
<div class="ti-modal-footer !border !border-defaultborder dark:!border-defaultborder/10 !py-[1rem] !px-[1.25rem] justify-end gap-2">
<button type="button" class="ti-btn ti-btn-light" (click)="sessionTimeoutService.logoutNow()">Log Out</button>
<button type="button" class="ti-btn ti-btn-primary-full" (click)="sessionTimeoutService.staySignedIn()">Stay Signed In</button>
</div>
</div>
</div>
</div>
}
@@ -1,143 +0,0 @@
import { Component, DOCUMENT, ElementRef, Renderer2, ViewChild, inject } from '@angular/core';
import { Menu, NavService } from '../../services/nav.service';
import { Router, RouterOutlet } from '@angular/router';
import { AppStateService } from '../../services/app-state.service';
import { Switcher } from '../../components/switcher/switcher';
import { Header } from '../../components/header/header';
import { Sidebar } from '../../components/sidebar/sidebar';
import { HoverEffectSidebarDirective } from '../../directives/hover-effect-sidebar.directive';
import { PageHeader } from '../../components/page-header/page-header';
import { Footer } from '../../components/footer/footer';
import { TabToTop } from '../../components/tab-to-top/tab-to-top';
import { AppLoader } from '../../components/app-loader/app-loader';
import { SessionTimeoutService } from '../../../core/services/session-timeout.service';
import { AppContextService } from '../../../core/services/app-context.service';
import { AuthService } from '../../../core/auth/auth.service';
@Component({
selector: 'app-content-layout',
templateUrl: './content-layout.html',
styleUrl: './content-layout.scss',
providers: [SessionTimeoutService],
imports: [
Switcher,
Header,
Sidebar,
HoverEffectSidebarDirective,
PageHeader,
RouterOutlet,
Footer,
TabToTop,
AppLoader,
],
})
export class ContentLayout {
navServices = inject(NavService);
sessionTimeoutService = inject(SessionTimeoutService);
private readonly appContextService = inject(AppContextService);
private readonly authService = inject(AuthService);
private document = inject<Document>(DOCUMENT);
private appStateService = inject(AppStateService);
private elementRef = inject(ElementRef);
private renderer = inject(Renderer2);
@ViewChild('responsiveoverlay') responsiveoverlay!: ElementRef<HTMLDivElement>;
public menuItems!: Menu[];
constructor() {
this.sessionTimeoutService.start();
if (this.authService.isLoggedIn) {
this.appContextService.ensureMenuInitialized().subscribe();
}
this.navServices.items.subscribe((menuItems: any) => {
this.menuItems = menuItems;
});
let html = this.document.documentElement;
this.appStateService.state$.subscribe(state => {
if (state) {
if (window.innerWidth <= 996) {
html?.setAttribute('data-toggled', html?.getAttribute('data-toggled') == 'close' ? 'close' : 'close');
}
if (state.menuStyles == 'menu-hover' || state.menuStyles == 'icon-hover') {
this.clearNavDropdown()
}
}
});
}
clearNavDropdown() {
this.menuItems?.forEach((a: any) => {
a.active = false;
a?.children?.forEach((b: any) => {
b.active = false;
b?.children?.forEach((c: any) => {
c.active = false;
});
});
});
}
clickOnBody() {
this.responsiveoverlay.nativeElement.classList.remove('active');
const htmlElement = this.document.documentElement;
this.renderer.removeAttribute(htmlElement, 'data-icon-overlay');
if (window.innerWidth <= 996) {
htmlElement?.setAttribute('data-toggled', htmlElement?.getAttribute('data-toggled') == 'close' ? 'close' : 'close');
}
const navStyle = htmlElement.getAttribute('data-nav-style');
if (htmlElement.getAttribute('data-toggled') == 'icon-text-close') {
this.renderer.removeAttribute(htmlElement, 'data-icon-text');
}
if (htmlElement.getAttribute('data-nav-layout') == 'horizontal'
&& window.innerWidth > 996) {
this.clearNavDropdown();
}
else
if (navStyle === 'menu-click' || navStyle === 'menu-hover' || navStyle === 'icon-click' || navStyle === 'icon-hover') {
document.querySelector('.double-menu-active')?.setAttribute('style', 'display: none;');
}
const switcher = this.elementRef.nativeElement.querySelector('.switcher');
if (switcher) {
this.renderer.removeClass(switcher, 'show');
this.responsiveoverlay.nativeElement.classList.add('active');
} else {
this.responsiveoverlay.nativeElement.classList.remove('active');
}
const sidebar = this.elementRef.nativeElement.querySelector('.sidebar');
if (sidebar) {
this.renderer.removeClass(sidebar, 'show');
}
}
closeMenu() {
this.menuItems?.forEach((a: any) => {
if (this.menuItems) {
a.active = false;
}
a?.children?.forEach((b: any) => {
if (a.children) {
b.active = false;
}
});
});
}
clearToggle() {
let html = this.elementRef.nativeElement.ownerDocument.documentElement;
html?.setAttribute('data-toggled', 'close');
document.querySelector('#responsive-overlay')?.classList.remove('active');
}
}
@@ -1,321 +0,0 @@
import { DOCUMENT, ElementRef, inject, Injectable, Renderer2 } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
interface StateType {
direction: string;
theme: string;
navigationStyles: string, // vertical, horizontal
menuStyles: string, // menu-click, menu-hover, icon-click, icon-hover
layoutStyles: string, // double-menu, detached, icon-overlay, icontext-menu, closed-menu, default-menu
pageStyles: string, // regular, classic, modern
widthStyles: string, // fullwidth, boxed
menuPosition: string, // fixed, scrollable
headerPosition: string, // fixed, scrollable
menuColor: string, // light, dark, color, gradient, transparent
headerColor: string, // light, dark, color, gradient, transparent
themePrimary: string, // '58, 88, 146', '92, 144, 163', '161, 90, 223', '78, 172, 76', '223, 90, 90'
themeBackground: string,
backgroundImage: string,
};
@Injectable({
providedIn: 'root'
})
export class AppStateService {
private readonly localStorageKey = 'Ynex-ng'; // Customize this key
private initialState: StateType = {
theme: 'light', // light, dark
direction: 'ltr', // ltr, rtl
navigationStyles: 'vertical', // vertical, horizontal
menuStyles: '', // menu-click, menu-hover, icon-click, icon-hover
layoutStyles: 'default', // double-menu, detached, icon-overlay, icontext-menu, closed-menu, default-menu
pageStyles: 'regular', // regular, classic, modern
widthStyles: 'fullwidth', // fullwidth, boxed
menuPosition: 'fixed', // fixed, scrollable
headerPosition: 'fixed', // fixed, scrollable
menuColor: 'dark', // light, dark, color, gradient, transparent
headerColor: 'light', // light, dark, color, gradient, transparent
themePrimary: '', // '58, 88, 146', '92, 144, 163', '161, 90, 223', '78, 172, 76', '223, 90, 90'
themeBackground: '',
backgroundImage: '', // bgimg1, bgimg2, bgimg3, bgimg4, bgimg5
} // Store initial state
private stateSubject = new BehaviorSubject<StateType>(this.initialState); // Use any for initial null value
state$ = this.stateSubject.asObservable();
private document = inject(DOCUMENT);
navigationStyles: any;
private html = this.document.documentElement;
constructor() {
const initialState: StateType = this.getInitialStateFromLocalStorage();
// this.initializeState();
this.stateSubject.next(initialState);
}
private getInitialStateFromLocalStorage(): StateType {
try {
const storedState = localStorage.getItem(this.localStorageKey);
if (storedState) {
return JSON.parse(storedState);
}
} catch (error) {
console.error('Error retrieving initial state from local storage:', error);
}
return this.initialState;
}
getupdateState() {
const currentState = this.stateSubject.getValue();
return currentState
}
updateState(newState?: Partial<any>) { // Use any for partial updates
const currentState = this.stateSubject.getValue(); // Get current state
if (!currentState) {
// Handle initial update case (no state emitted yet)
this.updateStateAndEmit(newState);
return;
}
if (newState) {
const updatedState = { ...currentState, ...newState }; // Merge updates
this.updateStateAndEmit(updatedState); // Update and emit combined state
} else {
this.updateStateAndEmit(currentState);
return;
}
}
private state: { [key: string]: any } = {};
getState(menuStyles: string): any {
return this.state[menuStyles];
}
private applyThemeBackgroundSpecificChanges(background: any) {
this.html?.style.setProperty('--color-bodybg', background.main);
this.html?.style.setProperty('--color-bodybg2', background.secondary);
this.html?.style.setProperty('--color-light', background.accent);
this.html?.style.setProperty('--color-formcontrolbg', `rgba(${background.accent})`);
this.html?.style.setProperty('--color-inputborder', background.overlay);
this.html?.style.setProperty('--color-gray3', background.primary);
this.applythemeSpecificChanges(background.theme);
}
private applyDirectionSpecificChanges(direction: string) {
this.html?.setAttribute('dir', direction);
}
private applythemeSpecificChanges(theme: string) {
this.html?.setAttribute('class', theme); //setting theme style
this.html?.setAttribute('data-header-styles', theme); //setting header style
}
private applyNavigationStylesSpecificChanges(navigationStyles: string) {
this.html?.setAttribute('data-nav-layout', navigationStyles);
if (navigationStyles == 'horizontal') {
this.html?.setAttribute('data-nav-style', 'menu-click');
this.html?.removeAttribute('data-vertical-style');
}
}
private applyMenuStylesSpecificChanges(menuStyles: string) {
this.html?.setAttribute('data-nav-style', menuStyles);
this.html?.setAttribute('data-toggled', menuStyles + '-closed');
this.html?.removeAttribute('data-vertical-style');
}
private applyLayoutStylesSpecificChanges(layoutStyles: string) {
this.html?.setAttribute('data-vertical-style', layoutStyles);
this.html?.removeAttribute('data-nav-style');
switch (layoutStyles) {
case 'default':
this.html?.setAttribute('data-vertical-style', 'overlay');
this.html?.setAttribute('data-toggled', '');
break;
case 'closed':
this.html?.setAttribute('data-toggled', 'close-menu-close');
break;
case 'icontext':
this.html?.setAttribute('data-toggled', 'icon-text-close');
break;
case 'overlay':
this.html?.setAttribute('data-toggled', 'icon-overlay-close');
break;
case 'detached':
this.html?.setAttribute('data-toggled', 'detached-close');
break;
case 'doublemenu':
this.html?.setAttribute('data-toggled', 'double-menu-open');
break;
}
if (layoutStyles === 'icon-text') {
this.html?.setAttribute('icon-text', 'open');
} else {
// If not 'icon-text', remove the icon-text attribute
this.html?.removeAttribute('icon-text');
}
}
private applypageStylesSpecificChanges(pageStyles: string) {
this.html?.setAttribute('data-page-style', pageStyles);
const slideRight = document.querySelector('.slide-right') as HTMLElement | null;
if (slideRight) {
// If the element exists, toggle the 'd-none' class
if (slideRight.classList.contains('d-none')) {
slideRight.classList.remove('d-none');
} else {
slideRight.classList.add('d-none');
}
} else {
// If the element does not exist (is null), create a safe fallback by adding 'd-none'
const dummySlideRight = document.createElement('div');
dummySlideRight.classList.add('slide-right', 'd-none'); // Add classes to the new element
document.body.appendChild(dummySlideRight); // Append it to the DOM as a fallback
}
}
private applywidthStylesSpecificChanges(widthStyles: string) {
this.html?.setAttribute('data-width', widthStyles);
}
private applymenuPositionSpecificChanges(menuPosition: string) {
this.html?.setAttribute('data-menu-position', menuPosition);
}
private applyheaderPositionSpecificChanges(headerPosition: string) {
this.html?.setAttribute('data-header-position', headerPosition);
}
private applyheaderColorSpecificChanges(headerColor: string) {
this.html?.setAttribute('data-header-styles', headerColor);
}
private applymenuColorSpecificChanges(menuColor: string) {
this.html?.setAttribute('data-menu-styles', menuColor);
}
private applyPrimarySpecificChanges(primary: string) {
this.html?.style.setProperty('--color-primaryrgb', primary);
this.html?.style.setProperty('--color-primary', primary);
}
private applybackgroundImageSpecificChanges(backgroundImage: string) {
this.html?.setAttribute('bg-img', backgroundImage);
}
public applyReset() {
if (this.html) {
this.html?.style.removeProperty('--color-bodybg');
this.html?.style.removeProperty('--color-gray3');
this.html?.style.removeProperty('--color-bodybg2');
this.html?.style.removeProperty('--color-light');
this.html?.style.removeProperty('--color-formcontrolbg');
this.html?.style.removeProperty('--color-inputborder');
this.html?.style.removeProperty('--color-primary');
this.html?.style.removeProperty('--color-primaryrgb');
}
this.html?.removeAttribute('bg-img');
this.html?.setAttribute('data-vertical-style', 'overlay');
this.stateSubject.next(this.initialState);
this.updateStateAndEmit(this.initialState);
localStorage.clear();
if (window.innerWidth <= 992) {
this.html?.setAttribute('data-toggled', 'close');
}
}
private updateStateAndEmit(state: any) {
// Conditional logic based on direction changes
const currentState = this.stateSubject.getValue(); // Get current state
// Conditional logic based on theme changes
if (state['theme']) {
this.applythemeSpecificChanges(state['theme']);
}
if (state['direction']) {
this.applyDirectionSpecificChanges(state['direction']);
}
// Conditional logic based on theme changes
if (state['navigationStyles']) {
this.applyNavigationStylesSpecificChanges(state['navigationStyles']);
}
// Conditional logic based on theme changes
if (state['menuStyles'] && !state['layoutStyles']) {
this.applyMenuStylesSpecificChanges(state['menuStyles']);
}
if (state['layoutStyles'] && !state['menuStyles']) {
this.applyLayoutStylesSpecificChanges(state['layoutStyles']);
}
if (state['pageStyles']) {
this.applypageStylesSpecificChanges(state['pageStyles']);
}
if (state['widthStyles']) {
this.applywidthStylesSpecificChanges(state['widthStyles']);
}
if (state['menuPosition']) {
this.applymenuPositionSpecificChanges(state['menuPosition']);
}
if (state['headerPosition']) {
this.applyheaderPositionSpecificChanges(state['headerPosition']);
}
if (state['themePrimary']) {
this.applyPrimarySpecificChanges(state['themePrimary']);
}
if (state['themeBackground']) {
this.applyThemeBackgroundSpecificChanges(state['themeBackground']);
}
if (state['headerColor']) {
this.applyheaderColorSpecificChanges(state['headerColor']);
}
if (state['menuColor']) {
this.applymenuColorSpecificChanges(state['menuColor']);
}
if (state['backgroundImage']) {
this.applybackgroundImageSpecificChanges(state['backgroundImage']);
}
this.stateSubject.next(state);
this.updateLocalStorage(state);
}
private updateLocalStorage(state: any) {
try {
localStorage.setItem(this.localStorageKey, JSON.stringify(state));
} catch (error) {
console.error('Error saving state to local storage:', error);
}
}
}
-152
View File
@@ -1,152 +0,0 @@
import { Injectable, NgZone, inject } from '@angular/core';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireAuth } from '@angular/fire/compat/auth';
import { Router } from '@angular/router';
import { environment } from '../../../environments/environment';
import { AngularFirestoreDocument } from '@angular/fire/compat/firestore';
export interface User {
uid: string;
email: string;
displayName: string;
photoURL: string;
emailVerified: boolean;
}
@Injectable({
providedIn: 'root',
})
export class AuthService {
private afu = inject(AngularFireAuth);
private router = inject(Router);
ngZone = inject(NgZone);
authState: any;
afAuth: any;
afs: any;
public showLoader:boolean=false;
constructor() {
this.afu.authState.subscribe((auth: any) => {
this.authState = auth;
});
}
// all firebase getdata functions
get isUserAnonymousLoggedIn(): boolean {
return this.authState !== null ? this.authState.isAnonymous : false;
}
get currentUserId(): string {
return this.authState !== null ? this.authState.uid : '';
}
get currentUserName(): string {
return this.authState['email'];
}
get currentUser(): any {
return this.authState !== null ? this.authState : null;
}
get isUserEmailLoggedIn(): boolean {
if (this.authState !== null && !this.isUserAnonymousLoggedIn) {
return true;
} else {
return false;
}
}
registerWithEmail(email: string, password: string) {
return this.afu
.createUserWithEmailAndPassword(email, password)
.then((user: any) => {
this.authState = user;
})
.catch((_error: any) => {
console.log(_error);
throw _error;
});
}
loginWithEmail(email: string, password: string) {
return this.afu
.signInWithEmailAndPassword(email, password)
.then((user: any) => {
this.authState = user;
})
.catch((_error: any) => {
console.log(_error);
throw _error;
});
}
singout(): void {
this.afu.signOut();
this.router.navigate(['/login']);
}
// Sign up with email/password
SignUp(email:any, password:any) {
return this.afAuth.createUserWithEmailAndPassword(email, password)
.then((result:any) => {
/* Call the SendVerificaitonMail() function when new user sign
up and returns promise */
this.SendVerificationMail();
this.SetUserData(result.user);
}).catch((error:any) => {
window.alert(error.message)
})
}
// main verification function
SendVerificationMail() {
return this.afAuth.currentUser.then((u:any) => u.sendEmailVerification()).then(() => {
this.router.navigate(['/dashboard']);
})
}
// Set user
SetUserData(user:any) {
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
const userData: User = {
email: user.email,
displayName: user.displayName,
uid: user.uid,
photoURL: user.photoURL || 'src/favicon.ico',
emailVerified: user.emailVerified
};
userRef.delete().then(function () {})
.catch(function (error:any) {});
return userRef.set(userData, {
merge: true
});
}
// sign in function
SignIn(email:any, password:any) {
return this.afAuth.signInWithEmailAndPassword(email, password)
.then((result:any) => {
if (result.user.emailVerified !== true) {
this.SetUserData(result.user);
this.SendVerificationMail();
this.showLoader = true;
} else {
this.showLoader = false;
this.ngZone.run(() => {
this.router.navigate(['/auth/login']);
});
}
}).catch((error:any) => {
throw error;
})
}
ForgotPassword(passwordResetEmail:any) {
return this.afAuth.sendPasswordResetEmail(passwordResetEmail)
.then(() => {
window.alert('Password reset email sent, check your inbox.');
}).catch((error:any) => {
window.alert(error);
});
}
}
@@ -1,27 +0,0 @@
import { Injectable } from '@angular/core';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { AngularFireDatabaseModule } from '@angular/fire/compat/database';
import { AngularFireAuthModule } from '@angular/fire/compat/auth';
import { environment } from '../../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class FirebaseService {
constructor() {
AngularFireModule.initializeApp(environment.firebase);
}
getFirestore() {
return AngularFirestoreModule;
}
getDatabase() {
return AngularFireDatabaseModule;
}
getAuth() {
return AngularFireAuthModule;
}
}
-102
View File
@@ -1,102 +0,0 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Subject, BehaviorSubject, fromEvent } from 'rxjs';
import { takeUntil, debounceTime } from 'rxjs/operators';
import { Router } from '@angular/router';
// Menu
export interface Menu {
headTitle?: string;
headTitle2?: string;
path?: string;
title?: string;
icon?: string;
type?: string;
badgeValue?: string;
badgeClass?: string;
badgeText?: string;
active?: boolean;
selected?: boolean;
bookmark?: boolean;
children?: Menu[];
children2?: Menu[];
Menusub?: boolean;
target?: boolean;
menutype?: string,
dirchange?: boolean,
nochild?: any
}
@Injectable({
providedIn: 'root',
})
export class NavService implements OnDestroy {
private unsubscriber: Subject<any> = new Subject();
public screenWidth: BehaviorSubject<number> = new BehaviorSubject(
window.innerWidth
);
// Search Box
public search = false;
// Language
public language = false;
// Mega Menu
public megaMenu = false;
public levelMenu = false;
public megaMenuColapse: boolean = window.innerWidth < 1199 ? true : false;
// Collapse Sidebar
public collapseSidebar: boolean = window.innerWidth < 991 ? true : false;
// For Horizontal Layout Mobile
public horizontal: boolean = window.innerWidth < 991 ? false : true;
// Full screen
public fullScreen = false;
active: any;
constructor(private router: Router) {
this.setScreenWidth(window.innerWidth);
fromEvent(window, 'resize')
.pipe(debounceTime(1000), takeUntil(this.unsubscriber))
.subscribe((evt: any) => {
this.setScreenWidth(evt.target.innerWidth);
if (evt.target.innerWidth < 991) {
this.collapseSidebar = true;
this.megaMenu = false;
this.levelMenu = false;
}
if (evt.target.innerWidth < 1199) {
this.megaMenuColapse = true;
}
});
if (window.innerWidth < 991) {
// Detect Route change sidebar close
this.router.events.subscribe((event) => {
this.collapseSidebar = true;
this.megaMenu = false;
this.levelMenu = false;
});
}
}
ngOnDestroy() {
this.unsubscriber.next;
this.unsubscriber.complete();
}
private setScreenWidth(width: number): void {
this.screenWidth.next(width);
}
items = new BehaviorSubject<Menu[]>([]);
setMenuItems(menuItems: Menu[]): void {
this.items.next(menuItems);
}
clearMenuItems(): void {
this.items.next([]);
}
}