Add new components, styles, and services for user management and navigation

This commit is contained in:
liosha84
2025-06-25 13:50:14 +03:00
parent 13a8ff4494
commit 5dc79438b7
42 changed files with 1439 additions and 0 deletions
@@ -0,0 +1,43 @@
@for (breadcrumb of navigationList; track breadcrumb; let last = $last) {
@if (last && breadcrumb.breadcrumbs !== false) {
<div class="page-header">
<div class="page-block">
<div class="row align-items-center">
<div class="col-md-12">
<ul class="breadcrumb">
<li class="breadcrumb-item">
@if (type === 'theme2') {
<a [routerLink]="['/main/home']" title="Home" class="home"><i class="feather icon-home"></i></a>
}
@if (type === 'theme1') {
<a [routerLink]="['/main/home']" class="home">Home</a>
}
</li>
@for (breadcrumb of navigationList; track breadcrumb) {
@if (breadcrumb.url !== false) {
<li class="breadcrumb-item">
<a [routerLink]="breadcrumb.url" class="f-14 f-w-600">{{ breadcrumb.title }}</a>
</li>
}
@if (breadcrumb.url === false && breadcrumb.type !== 'group') {
<li class="breadcrumb-item">
<a href="javascript:">{{ breadcrumb.title }}</a>
</li>
}
}
</ul>
</div>
<div class="col-md-12">
<div class="page-header-title">
@for (breadcrumb of navigationList; track breadcrumb; let last = $last) {
@if (last) {
<h2 class="mb-0 f-w-600 mt-2">{{ breadcrumb.title }}</h2>
}
}
</div>
</div>
</div>
</div>
</div>
}
}
@@ -0,0 +1,103 @@
// Angular Import
import { Component, Input, inject, input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NavigationEnd, Router, RouterModule, Event } from '@angular/router';
import { Title } from '@angular/platform-browser';
// project import
//import { NavigationItem, NavigationItems } from 'src/app/theme/layouts/admin-layout/navigation/navigation';
// icons
import { IconService } from '@ant-design/icons-angular';
import { GlobalOutline, NodeExpandOutline } from '@ant-design/icons-angular/icons';
import {NavigationItem, NavigationItems} from '../../layouts/admin-layout/navigation/navigation';
interface titleType {
// eslint-disable-next-line
url: any;
title: string;
breadcrumbs: unknown;
type: string;
link?: string | undefined;
description?: string | undefined;
path?: string | undefined;
}
@Component({
selector: 'app-breadcrumb',
imports: [CommonModule, RouterModule],
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss']
})
export class BreadcrumbComponent {
private route = inject(Router);
private titleService = inject(Title);
private iconService = inject(IconService);
// public props
@Input() type: string;
dashboard = input(true);
Component = input(false);
navigations: NavigationItem[];
ComponentNavigations: NavigationItem[] = [];
breadcrumbList: Array<string> = [];
navigationList!: titleType[];
componentList!: titleType[];
// constructor
constructor() {
this.navigations = NavigationItems;
this.type = 'theme1';
this.setBreadcrumb();
this.iconService.addIcon(...[GlobalOutline, NodeExpandOutline]);
}
// public method
setBreadcrumb() {
this.route.events.subscribe((router: Event) => {
if (router instanceof NavigationEnd) {
const activeLink = router.url;
const breadcrumbList = this.filterNavigation(this.navigations, activeLink);
this.navigationList = breadcrumbList;//breadcrumbList.slice(breadcrumbList.length - 1, breadcrumbList.length);
const title = breadcrumbList[breadcrumbList.length - 1]?.title || 'Welcome';
this.titleService.setTitle(title + ' | Mantis Angular Admin Template');
}
});
}
filterNavigation(navItems: NavigationItem[], activeLink: string): titleType[] {
for (const navItem of navItems) {
if (navItem.type === 'item' && 'url' in navItem && navItem.url === activeLink) {
return [
{
url: 'url' in navItem ? navItem.url : false,
title: navItem.title,
link: navItem.link,
description: navItem.description,
path: navItem.path,
breadcrumbs: 'breadcrumbs' in navItem ? navItem.breadcrumbs : true,
type: navItem.type
}
];
}
if ((navItem.type === 'group' || navItem.type === 'collapse') && 'children' in navItem) {
const breadcrumbList = this.filterNavigation(navItem.children!, activeLink);
if (breadcrumbList.length > 0) {
breadcrumbList.unshift({
url: 'url' in navItem ? navItem.url : false,
title: navItem.title,
link: navItem.link,
path: navItem.path,
description: navItem.description,
breadcrumbs: 'breadcrumbs' in navItem ? navItem.breadcrumbs : true,
type: navItem.type
});
return breadcrumbList;
}
}
}
return [];
}
}
@@ -0,0 +1,64 @@
.sk-line-material {
top: 0;
position: relative;
margin: auto;
width: 100%;
}
.sk-line-material .sk-child {
width: 100%;
height: 4px;
position: absolute;
top: 0;
display: inline-block;
transform-origin: 0% 0%;
-webkit-animation: sk-line-material 2s ease-in-out 0s infinite both;
animation: sk-line-material 2s ease-in-out 0s infinite both;
}
@-webkit-keyframes sk-line-material {
0%,
80%,
100% {
-webkit-transform: scaleX(0);
transform: scaleX(0);
}
40% {
-webkit-transform: scaleX(1);
transform: scaleX(1);
}
}
@keyframes sk-line-material {
0% {
-webkit-transform: scaleX(0);
transform: scaleX(0);
}
100% {
-webkit-transform: scaleX(1);
transform: scaleX(1);
}
}
#http-loader {
top: 0;
left: 0;
height: 100%;
width: 100%;
position: fixed;
z-index: 9999;
}
.loader-bg {
height: 100%;
width: 100%;
position: absolute;
filter: alpha(opacity=70);
opacity: 1;
background-color: rgba(0, 0, 0, 0);
}
.colored-parent,
.colored > div {
background-color: rgba(26, 188, 156, 0.8);
}
@@ -0,0 +1,11 @@
export const Spinkit = {
skChasingDots: 'sk-chasing-dots',
skCubeGrid: 'sk-cube-grid',
skDoubleBounce: 'sk-double-bounce',
skRotatingPlane: 'sk-rotationg-plane',
skSpinnerPulse: 'sk-spinner-pulse',
skThreeBounce: 'sk-three-bounce',
skWanderingCubes: 'sk-wandering-cubes',
skWave: 'sk-wave',
skLine: 'sk-line-material'
};
@@ -0,0 +1,11 @@
@if (isSpinnerVisible) {
<div id="http-loader">
<div class="loader-bg">
@if (spinner() === Spinkit.skLine) {
<div class="sk-line-material" [class.colored]="!backgroundColor()">
<div class="sk-child sk-bounce1" [style.background-color]="backgroundColor()"></div>
</div>
}
</div>
</div>
}
@@ -0,0 +1,22 @@
#http-loader {
top: 0;
left: 0;
height: 100%;
width: 100%;
position: fixed;
z-index: 9999;
}
.loader-bg {
height: 100%;
width: 100%;
position: absolute;
filter: alpha(opacity=70);
opacity: 0.7;
background-color: #f1f1f1;
}
.colored-parent,
.colored > div {
background-color: #333;
}
@@ -0,0 +1,46 @@
// Angular import
import { Component, OnDestroy, ViewEncapsulation, inject, input } from '@angular/core';
import { Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
import { DOCUMENT } from '@angular/common';
// project import
import { Spinkit } from './spinkits';
@Component({
selector: 'app-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss', './spinkit-css/sk-line-material.scss'],
encapsulation: ViewEncapsulation.None
})
export class SpinnerComponent implements OnDestroy {
private router = inject(Router);
private document = inject<Document>(DOCUMENT);
// public props
isSpinnerVisible = true;
Spinkit = Spinkit;
backgroundColor = input('#1890ff');
spinner = input(Spinkit.skLine);
// Constructor
constructor() {
this.router.events.subscribe(
(event) => {
if (event instanceof NavigationStart) {
this.isSpinnerVisible = true;
} else if (event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError) {
this.isSpinnerVisible = false;
}
},
() => {
this.isSpinnerVisible = false;
}
);
}
// life cycle event
ngOnDestroy(): void {
this.isSpinnerVisible = false;
}
}
@@ -0,0 +1,50 @@
<app-navigation
class="pc-sidebar"
[ngClass]="{
'navbar-collapsed': navCollapsed,
'mob-open': navCollapsedMob
}"
(NavCollapse)="this.navCollapsed = !this.navCollapsed"
/>
<app-nav-bar (NavCollapsedMob)="navMobClick()" (NavCollapse)="this.navCollapsed = !this.navCollapsed" />
<!--<app-nav-bar>
</app-nav-bar>-->
<!--<app-navigation></app-navigation>-->
<div class="pc-container">
<div class="coded-wrapper">
<div class="coded-content">
<div class="coded-inner-content">
<app-breadcrumb />
<div class="main-body">
<div class="page-wrapper">
<router-outlet />
</div>
</div>
</div>
</div>
</div>
<div class="pc-menu-overlay" (click)="closeMenu()" (keydown)="handleKeyDown($event)" tabindex="0"></div>
</div>
<footer class="pc-footer">
<div class="footer-wrapper container-fluid">
<div class="row">
<div class="col my-1">
<p class="m-0">
Copyright &copy;
<a href="https://codedthemes.com" target="_blank">CodedThemes</a>
</p>
</div>
<div class="col-auto my-1">
<ul class="list-inline footer-link mb-0">
<li class="list-inline-item"><a href="https://codedthemes.com/" target="_blank">Home</a></li>
<li class="list-inline-item">
<a href="https://codedthemes.com/privacy-policy/" target="_blank">Privacy Policy</a>
</li>
<li class="list-inline-item"><a href="https://codedthemes.support-hub.io/" target="_blank">Contact us</a></li>
</ul>
</div>
</div>
</div>
</footer>
<app-configuration />
@@ -0,0 +1,51 @@
// Angular import
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
// Project import
import { NavBarComponent } from './nav-bar/nav-bar.component';
import { NavigationComponent } from './navigation/navigation.component';
import { ConfigurationComponent } from './configuration/configuration.component';
import {BreadcrumbComponent} from '../../components/breadcrumb/breadcrumb.component';
//import { BreadcrumbComponent } from 'src/app/theme/shared/components/breadcrumb/breadcrumb.component';
@Component({
selector: 'app-admin',
imports: [CommonModule, BreadcrumbComponent, NavigationComponent, NavBarComponent, RouterModule, ConfigurationComponent, NavigationComponent],
templateUrl: './admin-layout.component.html',
styleUrls: ['./admin-layout.component.scss']
})
export class AdminComponent {
// public props
navCollapsed: boolean = true;
navCollapsedMob: boolean = true;
// public method
navMobClick() {
if (this.navCollapsedMob && !document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
this.navCollapsedMob = !this.navCollapsedMob;
setTimeout(() => {
this.navCollapsedMob = !this.navCollapsedMob;
}, 100);
} else {
this.navCollapsedMob = !this.navCollapsedMob;
}
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('navbar-collapsed')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('navbar-collapsed');
}
}
handleKeyDown(event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.closeMenu();
}
}
closeMenu() {
if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
document.querySelector('app-navigation.pc-sidebar')?.classList.remove('mob-open');
}
}
}
@@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-configuration',
imports: [],
templateUrl: './configuration.component.html',
styleUrl: './configuration.component.scss'
})
export class ConfigurationComponent {}
@@ -0,0 +1,10 @@
<header class="pc-header">
<div class="header-wrapper">
<div class="pc-mob-drp">
<app-nav-left [navCollapsed]="navCollapsed" (NavCollapse)="navCollapse()" (NavCollapsedMob)="navCollapseMob()"></app-nav-left>
</div>
<div>
<app-nav-right />
</div>
</div>
</header>
@@ -0,0 +1,3 @@
.pc-header{
left: 0;
}
@@ -0,0 +1,49 @@
// angular import
import {Component, inject, OnInit, output} from '@angular/core';
// project import
import { NavLeftComponent } from './nav-left/nav-left.component';
import { NavRightComponent } from './nav-right/nav-right.component';
import {DOCUMENT} from '@angular/common';
@Component({
selector: 'app-nav-bar',
imports: [NavLeftComponent, NavRightComponent],
templateUrl: './nav-bar.component.html',
styleUrls: ['./nav-bar.component.scss']
})
export class NavBarComponent implements OnInit{
private document = inject<Document>(DOCUMENT);
// public props
NavCollapse = output();
NavCollapsedMob = output();
navCollapsed: boolean = false;
windowWidth: number | undefined = 1000;
navCollapsedMob: boolean;
// Constructor
constructor() {
this.navCollapsedMob = false;
}
ngOnInit(): void {
this.windowWidth = this.document.defaultView?.innerWidth;
}
// public method
navCollapse() {
if (!!this.windowWidth && this.windowWidth >= 1025) {
this.navCollapsed = !this.navCollapsed;
this.NavCollapse.emit();
}
}
navCollapseMob() {
if (!!this.windowWidth && this.windowWidth < 1025) {
this.NavCollapsedMob.emit();
}
}
}
@@ -0,0 +1,43 @@
<ul class="list-unstyled">
<li class="pc-h-item pc-sidebar-collapse">
<a
href="javascript:"
title="click to menu collapse"
class="pc-head-link ms-0"
[ngClass]="{ on: navCollapsed() }"
id="sidebar-hide mobile-collapse"
(click)="navCollapse()"
>
@if (navCollapsed()) {
<i antIcon type="menu-unfold" theme="outline"></i>
} @else {
<i antIcon type="menu-fold" theme="outline"></i>
}
</a>
</li>
<li class="pc-h-item pc-sidebar-popup">
<a href="javascript:" class="pc-head-link ms-0" id="mobile-collapse1" (click)="this.NavCollapsedMob.emit()">
<i antIcon type="menu-fold" theme="outline"></i>
</a>
</li>
<li class="dropdown pc-h-item d-inline-flex d-md-none" ngbDropdown>
<a href="javascript:" class="pc-head-link dropdown-toggle arrow-none m-0" ngbDropdownToggle>
<i class="ti ti-search"></i>
</a>
<div class="dropdown-menu dropdowm-menu-end pc-h-dropdown drp-search" ngbDropdownMenu>
<form class="px-3">
<div class="form-group mb-0 d-flex align-items-center">
<i class="search f-12" antIcon type="search" theme="outline"></i>
<input type="search" class="form-control border-0 shadow-none" placeholder="Ctrl + K" />
</div>
</form>
</div>
</li>
<li class="pc-h-item d-none d-md-inline-flex">
<form class="header-search">
<i class="search f-12" antIcon type="search" theme="outline"></i>
<input type="search" class="form-control" placeholder="Ctrl + K" />
</form>
</li>
</ul>
@@ -0,0 +1,37 @@
// Angular import
import {CommonModule, DOCUMENT} from '@angular/common';
import { Component, inject, input, output } from '@angular/core';
// project import
// icons
import { IconService, IconDirective } from '@ant-design/icons-angular';
import { MenuUnfoldOutline, MenuFoldOutline, SearchOutline } from '@ant-design/icons-angular/icons';
@Component({
selector: 'app-nav-left',
imports: [IconDirective, CommonModule],
templateUrl: './nav-left.component.html',
styleUrls: ['./nav-left.component.scss']
})
export class NavLeftComponent {
private iconService = inject(IconService);
private document = inject(DOCUMENT);
// public props
navCollapsed = input.required<boolean>();
NavCollapse = output();
NavCollapsedMob = output();
windowWidth: number | undefined = 1000;
// Constructor
constructor() {
this.windowWidth = this.document.defaultView?.innerWidth;
this.iconService.addIcon(...[MenuUnfoldOutline, MenuFoldOutline, SearchOutline]);
}
// public method
navCollapse() {
this.NavCollapse.emit();
}
}
@@ -0,0 +1,147 @@
<ul class="list-unstyled">
<li class="pc-h-item">
<a href="https://github.com/codedthemes/mantis-free-angular-admin-template" target="_blank" class="pc-head-link me-0 bg-gray-200">
<i antIcon type="github" theme="outline"></i>
</a>
</li>
<li class="dropdown pc-h-item" ngbDropdown>
<a href="javascript:" class="pc-head-link dropdown-toggle arrow-none me-0 bg-gray-200" data-bs-toggle="dropdown" ngbDropdownToggle>
<i antIcon type="bell" theme="outline"></i>
<span class="badge bg-primary pc-h-badge">3</span>
</a>
<div class="dropdown-menu dropdown-notification dropdown-menu-end pc-h-dropdown" ngbDropdownMenu>
<div class="dropdown-header d-flex align-items-center justify-content-between">
<h5 class="m-0">Notification</h5>
<a class="bg-transparent"><i class="text-success d-flex f-20" antIcon theme="outline" type="check-circle"></i></a>
</div>
<div class="dropdown-divider mb-0"></div>
<ng-scrollbar style="min-height: 280px" visibility="hover">
<div class="dropdown-header p-0 text-wrap header-notification-scroll">
<div class="list-group list-group-flush w-100">
<a class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="user-avatar bg-light-success"><i class="d-flex" antIcon theme="outline" type="gift"></i></div>
</div>
<div class="flex-grow-1 ms-3">
<span class="float-end text-muted">3:00 AM</span>
<p class="text-body mb-0">
It's
<b>Cristina danny's</b>
birthday today.
</p>
<span class="text-muted">2 min ago</span>
</div>
</div>
</a>
<a class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="user-avatar bg-light-primary"><i class="d-flex" antIcon theme="outline" type="message"></i></div>
</div>
<div class="flex-grow-1 ms-3">
<span class="float-end text-muted">6:00 PM</span>
<p class="text-body mb-1">
<b>Aida Burg</b>
commented your post.
</p>
<span class="text-muted">5 August</span>
</div>
</div>
</a>
<a class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="user-avatar bg-light-danger"><i class="d-flex" antIcon theme="outline" type="setting"></i></div>
</div>
<div class="flex-grow-1 ms-3">
<span class="float-end text-muted">2:45 PM</span>
<p class="text-body mb-1">
Your Profile is Complete &nbsp;
<b>60%</b>
</p>
<span class="text-muted">7 hours ago</span>
</div>
</div>
</a>
<a class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="user-avatar bg-light-primary"><i class="d-flex" antIcon theme="outline" type="phone"></i></div>
</div>
<div class="flex-grow-1 ms-3">
<span class="float-end text-muted">9:10 PM</span>
<p class="text-body mb-1">
<b>Cristina Danny</b>
invited to join
<b>Meeting.</b>
</p>
<span class="text-muted">Daily scrum meeting time</span>
</div>
</div>
</a>
</div>
</div>
</ng-scrollbar>
<div class="dropdown-divider mt-0"></div>
<div class="text-center py-2">
<a href="javascript:" class="link-primary">View all</a>
</div>
</div>
</li>
<li class="dropdown pc-h-item header-user-profile" ngbDropdown>
<a href="javascript:" class="pc-head-link dropdown-toggle arrow-none me-0" ngbDropdownToggle>
<img src="assets/images/user/avatar-2.jpg" alt="user-image" class="user-avatar me-2" />
<span class="f-w-600">John Doe</span>
</a>
<div class="dropdown-menu dropdown-user-profile dropdown-menu-end pc-h-dropdown" ngbDropdownMenu>
<div class="dropdown-header">
<div class="d-flex mb-1">
<div class="flex-shrink-0">
<img src="assets/images/user/avatar-2.jpg" alt="user-image" class="user-avatar wid-35" />
</div>
<div class="flex-grow-1 ms-3 text-start">
<h6 class="mb-0">JWT User</h6>
<span>UI/UX Designer</span>
</div>
<a href="javascript:" class="bg-transparent"><i class="d-flex f-20" antIcon theme="outline" type="logout"></i></a>
</div>
</div>
<ul ngbNav #nav="ngbNav" class="nav drp-tabs nav-fill nav-tabs">
<li ngbNavItem="1">
<a ngbNavLink>
<i antIcon theme="outline" type="user" class="me-2"></i>
Profile
</a>
<ng-template ngbNavContent>
@for (task of profile; track task) {
<div>
<a href="javascript:" class="dropdown-item">
<i class="text-muted" antIcon theme="outline" type="{{ task.icon }}"></i>
<span>{{ task.title }}</span>
</a>
</div>
}
</ng-template>
</li>
<li ngbNavItem="2">
<a ngbNavLink>
<i class="me-2" antIcon theme="outline" type="setting"></i>
Setting
</a>
<ng-template ngbNavContent>
@for (task of setting; track task) {
<div>
<a href="javascript:" class="dropdown-item">
<i class="text-muted" antIcon theme="outline" type="{{ task.icon }}"></i>
<span>{{ task.title }}</span>
</a>
</div>
}
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav"></div>
</div>
</li>
</ul>
@@ -0,0 +1,121 @@
// angular import
import {Component, inject, input, OnInit, output} from '@angular/core';
import { RouterModule } from '@angular/router';
// project import
// icon
import { IconService, IconDirective } from '@ant-design/icons-angular';
import {
BellOutline,
SettingOutline,
GiftOutline,
MessageOutline,
PhoneOutline,
CheckCircleOutline,
LogoutOutline,
EditOutline,
UserOutline,
ProfileOutline,
WalletOutline,
QuestionCircleOutline,
LockOutline,
CommentOutline,
UnorderedListOutline,
ArrowRightOutline,
GithubOutline
} from '@ant-design/icons-angular/icons';
import { NgbDropdownModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgScrollbarModule } from 'ngx-scrollbar';
import {DOCUMENT} from '@angular/common';
@Component({
selector: 'app-nav-right',
imports: [IconDirective, RouterModule, NgScrollbarModule, NgbNavModule, NgbDropdownModule],
templateUrl: './nav-right.component.html',
styleUrls: ['./nav-right.component.scss']
})
export class NavRightComponent implements OnInit{
private iconService = inject(IconService);
private document = inject(DOCUMENT);
//private window = inject(Window);
styleSelectorToggle = input<boolean>();
Customize = output();
windowWidth: number | undefined = 1000;
screenFull: boolean = true;
constructor() {
this.iconService.addIcon(
...[
CheckCircleOutline,
GiftOutline,
MessageOutline,
SettingOutline,
PhoneOutline,
LogoutOutline,
UserOutline,
EditOutline,
ProfileOutline,
QuestionCircleOutline,
LockOutline,
CommentOutline,
UnorderedListOutline,
ArrowRightOutline,
BellOutline,
GithubOutline,
WalletOutline
]
);
}
ngOnInit(): void {
this.windowWidth = this.document.defaultView?.innerWidth;
}
profile = [
{
icon: 'edit',
title: 'Edit Profile'
},
{
icon: 'user',
title: 'View Profile'
},
{
icon: 'profile',
title: 'Social Profile'
},
{
icon: 'wallet',
title: 'Billing'
},
{
icon: 'logout',
title: 'Logout'
}
];
setting = [
{
icon: 'question-circle',
title: 'Support'
},
{
icon: 'user',
title: 'Account Settings'
},
{
icon: 'lock',
title: 'Privacy Center'
},
{
icon: 'comment',
title: 'Feedback'
},
{
icon: 'unordered-list',
title: 'History'
}
];
}
@@ -0,0 +1,21 @@
<li data-username="mantis dashboard" class="nav-item coded-hasmenu" [routerLinkActive]="['active']">
<a [routerLinkActive]="['active']" (click)="navCollapse($event)">
@if (item.icon) {
<span class="coded-micon">
<i antIcon type="{{ item.icon }}" theme="outline"></i>
</span>
}
<span class="coded-mtext">
{{ item.title }}
</span>
</a>
<ul class="coded-submenu" [routerLinkActive]="['active']" [ngClass]="item.classes!">
@for (item of item.children; track item) {
@if (item.type === 'item') {
<app-nav-item [item]="item"></app-nav-item>
} @else if (item.type === 'collapse') {
<app-nav-collapse [item]="item"></app-nav-collapse>
}
}
</ul>
</li>
@@ -0,0 +1,88 @@
// Angular import
import {Component, inject, Input, OnInit, output} from '@angular/core';
import { animate, style, transition, trigger } from '@angular/animations';
import {CommonModule, DOCUMENT} from '@angular/common';
import { RouterModule } from '@angular/router';
// project import
import { NavigationItem } from '../../navigation';
import { NavItemComponent } from '../nav-item/nav-item.component';
import { IconDirective } from '@ant-design/icons-angular';
@Component({
selector: 'app-nav-collapse',
imports: [CommonModule, IconDirective, RouterModule, NavItemComponent],
templateUrl: './nav-collapse.component.html',
styleUrls: ['./nav-collapse.component.scss'],
animations: [
trigger('slideInOut', [
transition(':enter', [
style({ transform: 'translateY(-100%)', display: 'block' }),
animate('250ms ease-in', style({ transform: 'translateY(0%)' }))
]),
transition(':leave', [animate('250ms ease-in', style({ transform: 'translateY(-100%)' }))])
])
]
})
export class NavCollapseComponent implements OnInit{
private document = inject(DOCUMENT);
// public props
// Compact Menu in use For Sub Child Open in sidebar menu
showCollapseItem = output();
// all Version Get Item(Component Name Take)
@Input() item!: NavigationItem;
windowWidth: number | undefined = 1000;
// Constructor
constructor() {
//this.windowWidth = window.innerWidth;
}
ngOnInit(): void {
this.windowWidth = this.document.defaultView?.innerWidth;throw new Error('Method not implemented.');
}
// public method
navCollapse(e: MouseEvent) {
let parent = e.target as HTMLElement;
if (parent?.tagName === 'SPAN') {
parent = parent.parentElement!;
}
parent = (parent as HTMLElement).parentElement as HTMLElement;
const sections = document.querySelectorAll('.coded-hasmenu');
for (let i = 0; i < sections.length; i++) {
if (sections[i] !== parent) {
sections[i].classList.remove('coded-trigger');
}
}
let first_parent = parent.parentElement;
let pre_parent = ((parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
if (first_parent?.classList.contains('coded-hasmenu')) {
do {
first_parent?.classList.add('coded-trigger');
first_parent = ((first_parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
} while (first_parent.classList.contains('coded-hasmenu'));
} else if (pre_parent.classList.contains('coded-submenu')) {
do {
pre_parent?.parentElement?.classList.add('coded-trigger');
pre_parent = (((pre_parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
} while (pre_parent.classList.contains('coded-submenu'));
}
parent.classList.toggle('coded-trigger');
}
// for Compact Menu
subMenuCollapse(item: void) {
this.showCollapseItem.emit(item);
}
}
@@ -0,0 +1,34 @@
<!-- Vertical layout -->
<ng-scrollbar style="height: calc(100vh - 49px)" visibility="hover" id="nav-ps-mantis">
<div class="navbar-content">
<ul class="nav coded-inner-navbar" (clickOutside)="fireOutClick()" (mouseleave)="navMob()">
@for (item of navigations; track item) {
@if (item.type === 'group') {
<app-nav-group [item]="item"></app-nav-group>
}
}
</ul>
<div class="m-4 mb-0">
<div class="card nav-card">
<div class="card-body">
<div class="d-flex flex-column align-items-center">
<img src="assets/images/avatar-group.png" alt="user" class="img-fluid" />
<div class="m-t-20 text-center">
<h5 class="mb-0">Mantis Pro</h5>
<span class="text-muted">Checkout pro features</span>
</div>
<div class="m-t-20 text-center">
<a
href="https://codedthemes.com/item/mantis-angular-admin-template/?utm_source=free_demo&utm_medium=codedthemes&utm_campaign=button_download_premium"
target="_blank"
class="btn btn-primary text-white"
>
Pro
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-scrollbar>
@@ -0,0 +1,106 @@
// Angular import
import { Component, OnInit, inject, output } from '@angular/core';
import {CommonModule, DOCUMENT, Location, LocationStrategy} from '@angular/common';
import { RouterModule } from '@angular/router';
// project import
import { NavigationItem, NavigationItems } from '../navigation';
//import { environment } from 'src/environments/environment';
import { NavGroupComponent } from './nav-group/nav-group.component';
// icon
import { IconService } from '@ant-design/icons-angular';
import {
DashboardOutline,
CreditCardOutline,
LoginOutline,
QuestionOutline,
ChromeOutline,
FontSizeOutline,
ProfileOutline,
BgColorsOutline,
AntDesignOutline
} from '@ant-design/icons-angular/icons';
import { NgScrollbarModule } from 'ngx-scrollbar';
@Component({
selector: 'app-nav-content',
imports: [CommonModule, RouterModule, NavGroupComponent, NgScrollbarModule],
templateUrl: './nav-content.component.html',
styleUrls: ['./nav-content.component.scss']
})
export class NavContentComponent implements OnInit {
private location = inject(Location);
private locationStrategy = inject(LocationStrategy);
private iconService = inject(IconService)
private document = inject(DOCUMENT);
// public props
NavCollapsedMob = output();
navigations: NavigationItem[];
// version
title = 'Demo application for version numbering';
//currentApplicationVersion = environment.appVersion;
navigation = NavigationItems;
windowWidth: number | undefined = 1000;
// Constructor
constructor() {
this.iconService.addIcon(
...[
DashboardOutline,
CreditCardOutline,
FontSizeOutline,
LoginOutline,
ProfileOutline,
BgColorsOutline,
AntDesignOutline,
ChromeOutline,
QuestionOutline
]
);
this.navigations = NavigationItems;
}
// Life cycle events
ngOnInit() {
if (!!this.windowWidth && this.windowWidth < 1025) {
(this.document.querySelector('.coded-navbar') as HTMLDivElement)?.classList.add('menupos-static');
}
}
fireOutClick() {
let current_url = this.location.path();
const baseHref = this.locationStrategy.getBaseHref();
if (baseHref) {
current_url = baseHref + this.location.path();
}
const link = "a.nav-link[ href='" + current_url + "' ]";
const ele = document?.querySelector(link);
if (ele !== null && ele !== undefined) {
const parent = ele.parentElement;
const up_parent = parent?.parentElement?.parentElement;
const last_parent = up_parent?.parentElement;
if (parent?.classList.contains('coded-hasmenu')) {
parent.classList.add('coded-trigger');
parent.classList.add('active');
} else if (up_parent?.classList.contains('coded-hasmenu')) {
up_parent.classList.add('coded-trigger');
up_parent.classList.add('active');
} else if (last_parent?.classList.contains('coded-hasmenu')) {
last_parent.classList.add('coded-trigger');
last_parent.classList.add('active');
}
}
}
navMob() {
if (!!this.windowWidth && this.windowWidth < 1025 && document.querySelector('app-navigation.coded-navbar')?.classList.contains('mob-open')) {
this.NavCollapsedMob.emit();
}
}
}
@@ -0,0 +1,10 @@
<li class="nav-item coded-menu-caption">
<label>{{ item().title }}</label>
</li>
@for (item of item().children; track item) {
@if (item.type === 'collapse') {
<app-nav-collapse [item]="item"></app-nav-collapse>
} @else if (item.type === 'item') {
<app-nav-item [item]="item"></app-nav-item>
}
}
@@ -0,0 +1,67 @@
// Angular import
import { Component, OnInit, inject, input } from '@angular/core';
import {CommonModule, DOCUMENT, Location} from '@angular/common';
// project import
import { NavigationItem } from '../../navigation';
import { NavCollapseComponent } from '../nav-collapse/nav-collapse.component';
import { NavItemComponent } from '../nav-item/nav-item.component';
@Component({
selector: 'app-nav-group',
imports: [CommonModule, NavCollapseComponent, NavItemComponent],
templateUrl: './nav-group.component.html',
styleUrls: ['./nav-group.component.scss']
})
export class NavGroupComponent implements OnInit {
private location = inject(Location);
private document = inject(DOCUMENT);
// public props
// All Version in Group Name
item = input.required<NavigationItem>();
// Life cycle events
ngOnInit() {
// at reload time active and trigger link
let current_url: string | any = this.location.path();
// eslint-disable-next-line
// @ts-ignore
if (this.location['_baseHref']) {
// eslint-disable-next-line
// @ts-ignore
current_url = this.location['_baseHref'] + this.location.path();
}
const link = "a.nav-link[ href='" + current_url + "' ]";
//if(this.document === undefined) return;
const ele = this.document?.querySelector(link);
if (ele !== null && ele !== undefined) {
const parent = ele.parentElement;
const up_parent = parent?.parentElement?.parentElement;
const pre_parent = up_parent?.parentElement;
const last_parent = up_parent?.parentElement?.parentElement?.parentElement?.parentElement;
if (parent?.classList.contains('coded-hasmenu')) {
parent?.classList.add('coded-trigger');
parent?.classList.add('active');
} else if (up_parent?.classList.contains('coded-hasmenu')) {
up_parent?.classList.add('coded-trigger');
up_parent?.classList.add('active');
} else if (pre_parent?.classList.contains('coded-hasmenu')) {
pre_parent?.classList.add('coded-trigger');
pre_parent?.classList.add('active');
}
if (last_parent?.classList.contains('coded-hasmenu')) {
last_parent?.classList.add('coded-trigger');
if (pre_parent?.classList.contains('coded-hasmenu')) {
pre_parent?.classList.add('coded-trigger');
}
}
last_parent?.classList.add('active');
}
}
}
@@ -0,0 +1,32 @@
@if (item.url && !item.external) {
<li [ngClass]="item.classes!" [routerLinkActive]="['active']">
<a [target]="item.target ? '_blank' : '_self'" [routerLink]="[item.url]" (click)="closeOtherMenu($event)">
@if (item.icon) {
<span class="coded-micon">
<i antIcon type="{{ item.icon }}" theme="outline"></i>
</span>
}
@if (item.icon) {
<span class="coded-mtext">{{ item.title }}</span>
} @else {
{{ item.title }}
}
</a>
</li>
}
@if (item.url && item.external) {
<li [ngClass]="item.classes!">
<a [target]="item.target ? '_blank' : '_self'" [href]="item.url">
@if (item.icon) {
<span class="coded-micon">
<i antIcon type="{{ item.icon }}" theme="outline"></i>
</span>
}
@if (item.icon) {
<span class="coded-mtext">{{ item.title }}</span>
} @else {
{{ item.title }}
}
</a>
</li>
}
@@ -0,0 +1,51 @@
// Angular import
import {Component, inject, Input} from '@angular/core';
import {CommonModule, DOCUMENT} from '@angular/common';
import { RouterModule } from '@angular/router';
// Project import
import { NavigationItem } from '../../navigation';
import { IconDirective } from '@ant-design/icons-angular';
@Component({
selector: 'app-nav-item',
imports: [CommonModule, IconDirective, RouterModule],
templateUrl: './nav-item.component.html',
styleUrls: ['./nav-item.component.scss']
})
export class NavItemComponent {
private document = inject(DOCUMENT);
// public props
@Input() item!: NavigationItem;
// public method
closeOtherMenu(event: MouseEvent) {
const ele = event.target as HTMLElement;
if (ele !== null && ele !== undefined) {
const parent = ele.parentElement as HTMLElement;
const up_parent = ((parent.parentElement as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
const last_parent = up_parent.parentElement;
const sections = this.document.querySelectorAll('.coded-hasmenu');
for (let i = 0; i < sections.length; i++) {
sections[i].classList.remove('active');
sections[i].classList.remove('coded-trigger');
}
if (parent.classList.contains('coded-hasmenu')) {
parent.classList.add('coded-trigger');
parent.classList.add('active');
} else if (up_parent.classList.contains('coded-hasmenu')) {
up_parent.classList.add('coded-trigger');
up_parent.classList.add('active');
} else if (last_parent?.classList.contains('coded-hasmenu')) {
last_parent.classList.add('coded-trigger');
last_parent.classList.add('active');
}
}
if ((document.querySelector('app-navigation.pc-sidebar') as HTMLDivElement).classList.contains('mob-open')) {
(document.querySelector('app-navigation.pc-sidebar') as HTMLDivElement).classList.remove('mob-open');
}
}
}
@@ -0,0 +1,10 @@
<nav>
<div class="navbar-wrapper">
<div class="m-header">
<a href="javascript:" class="b-brand">
<img src="assets/images/logo-dark.svg" alt="theme-logo" class="logo logo-dark logo-lg" />
</a>
</div>
<app-nav-content (NavCollapsedMob)="navCollapseMob()" class="scroll-div w-100 compact"></app-nav-content>
</div>
</nav>
@@ -0,0 +1,49 @@
// Angular import
import {Component, EventEmitter, inject, OnInit, Output, output} from '@angular/core';
import {CommonModule, DOCUMENT} from '@angular/common';
import {NavContentComponent} from './nav-content/nav-content.component';
// project import
//import { NavContentComponent } from './nav-content/nav-content.component';
@Component({
selector: 'app-navigation',
// imports: [ CommonModule],
templateUrl: './navigation.component.html',
imports: [
NavContentComponent
],
styleUrls: ['./navigation.component.scss']
})
export class NavigationComponent implements OnInit{
private document = inject<Document>(DOCUMENT);
// media 1025 After Use Menu Open
NavCollapsedMob = output();
navCollapsedMob:boolean;
windowWidth: number | undefined = 1000;
// Constructor
constructor() {
//window.
// this.windowWidth = !document ? 0 : !document.defaultView ? 0 : document.defaultView?.innerWidth;
//this.windowWidth = document.defaultView !== null? document.defaultView.innerWidth : 1000;//window.innerWidth;
this.navCollapsedMob = false;
}
ngOnInit(): void {
this.windowWidth = this.document.defaultView?.innerWidth;
}
// public method
// @Output() NavCollapse = new EventEmitter<unknown>();
@Output() NavCollapse = new EventEmitter<unknown>();
navCollapseMob() {
if (!!this.windowWidth && this.windowWidth < 1025) {
this.NavCollapsedMob.emit();
}
}
}
@@ -0,0 +1,140 @@
export interface NavigationItem {
id: string;
title: string;
type: 'item' | 'collapse' | 'group';
translate?: string;
icon?: string;
hidden?: boolean;
url?: string;
classes?: string;
groupClasses?: string;
exactMatch?: boolean;
external?: boolean;
target?: boolean;
breadcrumbs?: boolean;
children?: NavigationItem[];
link?: string;
description?: string;
path?: string;
}
export const NavigationItems: NavigationItem[] = [
{
id: 'admin',
title: 'Admin',
type: 'group',
icon: 'icon-navigation',
url: '/main/admin/welcome',
target: true,
children: [
{
id: 'settings',
title: 'Settings',
type: 'item',
classes: 'nav-item',
url: '/main/admin/settings',
icon: 'dashboard',
// breadcrumbs: false
},
{
id: 'system',
title: 'System',
type: 'item',
classes: 'nav-item',
url: '/main/admin/system',
icon: 'dashboard',
// breadcrumbs: false
}
]
}/*,
{
id: 'authentication',
title: 'Authentication',
type: 'group',
icon: 'icon-navigation',
children: [
{
id: 'login',
title: 'Login',
type: 'item',
classes: 'nav-item',
url: '/login',
icon: 'login',
target: true,
breadcrumbs: false
},
{
id: 'register',
title: 'Register',
type: 'item',
classes: 'nav-item',
url: '/register',
icon: 'profile',
target: true,
breadcrumbs: false
}*/
// ]
// }
/*,
{
id: 'utilities',
title: 'UI Components',
type: 'group',
icon: 'icon-navigation',
children: [
{
id: 'typography',
title: 'Typography',
type: 'item',
classes: 'nav-item',
url: '/typography',
icon: 'font-size'
},
{
id: 'color',
title: 'Colors',
type: 'item',
classes: 'nav-item',
url: '/color',
icon: 'bg-colors'
},
{
id: 'ant-icons',
title: 'Ant Icons',
type: 'item',
classes: 'nav-item',
url: 'https://ant.design/components/icon',
icon: 'ant-design',
target: true,
external: true
}
]
},
{
id: 'other',
title: 'Other',
type: 'group',
icon: 'icon-navigation',
children: [
{
id: 'sample-page',
title: 'Sample Page',
type: 'item',
url: '/sample-page',
classes: 'nav-item',
icon: 'chrome'
},
{
id: 'document',
title: 'Document',
type: 'item',
classes: 'nav-item',
url: 'https://codedthemes.gitbook.io/mantis-angular/',
icon: 'question',
target: true,
external: true
}
]
}*/
];
@@ -0,0 +1 @@
<router-outlet></router-outlet>
@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
selector: 'app-guest-layout',
imports: [RouterModule],
templateUrl: './guest-layout.component.html',
styleUrl: './guest-layout.component.scss'
})
export class GuestLayoutComponent {}