diff --git a/code/UI/src/app/layout/main-layout/main-layout.component.html b/code/UI/src/app/layout/main-layout/main-layout.component.html
index 4b2e9f51..bfcfe49e 100644
--- a/code/UI/src/app/layout/main-layout/main-layout.component.html
+++ b/code/UI/src/app/layout/main-layout/main-layout.component.html
@@ -5,76 +5,11 @@
-
-
+
+
diff --git a/code/UI/src/app/layout/main-layout/main-layout.component.scss b/code/UI/src/app/layout/main-layout/main-layout.component.scss
index c7b3c8e4..cf5744ea 100644
--- a/code/UI/src/app/layout/main-layout/main-layout.component.scss
+++ b/code/UI/src/app/layout/main-layout/main-layout.component.scss
@@ -28,6 +28,18 @@
overflow-y: auto;
z-index: 10;
+ // Top color line
+ .sidebar-top-line {
+ height: 3px;
+ background: linear-gradient(to right, #673ab7, #9b59b6);
+ margin-bottom: 10px;
+ }
+
+ // Hide on mobile screens
+ @media (max-width: 768px) {
+ display: none;
+ }
+
// Collapsed state
&.collapsed {
width: 70px;
@@ -1286,75 +1298,51 @@
flex-direction: column;
height: 100%;
background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%);
-
+ overflow-y: auto;
+
.sidebar-top-line {
position: absolute;
top: 0;
left: 0;
right: 0;
- height: 1px;
+ height: 3px;
background: linear-gradient(90deg, rgba(142, 68, 173, 0.6), rgba(103, 58, 183, 0.6));
z-index: 2;
box-shadow: 0 0 8px rgba(142, 68, 173, 0.4);
}
-
- .logo-container {
- display: flex;
- align-items: center;
- padding: 15px 20px 10px;
- border-bottom: none;
- position: relative;
-
- .logo {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 40px;
- height: 40px;
- margin-right: 15px;
- border-radius: 50%;
- background-color: rgba(142, 68, 173, 0.1);
- border: 1px solid rgba(142, 68, 173, 0.3);
- animation: logo-glow 10s infinite ease-in-out;
-
- i {
- font-size: 20px;
- color: #ffffff;
- }
- }
-
- h1 {
- font-size: 22px;
- font-weight: 700;
- margin: 0;
- color: #fff;
- text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- letter-spacing: 0.5px;
- background: linear-gradient(to right, #fff, #bbb);
- -webkit-background-clip: text;
- background-clip: text;
- -webkit-text-fill-color: transparent;
- }
- }
}
/* Media queries for responsiveness */
@media screen and (max-width: 768px) {
+ .layout-main {
+ flex-direction: column;
+ }
+
.layout-content {
+ width: 100%;
+
.layout-content-inner {
padding: 1rem;
}
+
+ .top-bar {
+ .topbar-section {
+ &.center {
+ display: none; // Hide version bar on mobile to save space
+ }
+ }
+ }
}
-
- // Optimize the PrimeNG sidebar for mobile
+
+ // Style PrimeNG mobile sidebar
:host ::ng-deep {
- .p-sidebar.p-component.mobile-sidebar {
- background: linear-gradient(135deg, #2c1a47 0%, #1a0e29 50%, #1e1230 75%, rgba(30, 18, 48, 0.95) 100%) !important;
- border: none !important;
- box-shadow: -5px 0 20px rgba(0, 0, 0, 0.5) !important;
-
+ .p-sidebar.mobile-sidebar {
+ .p-sidebar-header {
+ display: none; // Hide the header since we're not using it
+ }
+
.p-sidebar-content {
- padding: 0 !important;
+ padding: 0;
}
}
}
diff --git a/code/UI/src/app/layout/main-layout/main-layout.component.ts b/code/UI/src/app/layout/main-layout/main-layout.component.ts
index bbab9860..0c922eb0 100644
--- a/code/UI/src/app/layout/main-layout/main-layout.component.ts
+++ b/code/UI/src/app/layout/main-layout/main-layout.component.ts
@@ -1,5 +1,5 @@
import { Component, inject, signal } from '@angular/core';
-import { Router, RouterOutlet, RouterLink } from '@angular/router';
+import { Router, RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Title } from '@angular/platform-browser';
@@ -12,6 +12,9 @@ import { SidebarModule } from 'primeng/sidebar';
import { DividerModule } from 'primeng/divider';
import { RippleModule } from 'primeng/ripple';
+// Custom Components
+import { SidebarContentComponent } from '../sidebar-content/sidebar-content.component';
+
interface MenuItem {
label: string;
icon: string;
@@ -25,14 +28,14 @@ interface MenuItem {
imports: [
CommonModule,
RouterOutlet,
- RouterLink,
ButtonModule,
ToolbarModule,
FormsModule,
MenuModule,
SidebarModule,
DividerModule,
- RippleModule
+ RippleModule,
+ SidebarContentComponent
],
templateUrl: './main-layout.component.html',
styleUrl: './main-layout.component.scss'
@@ -56,6 +59,16 @@ export class MainLayoutComponent {
constructor() {}
+ /**
+ * Handles mobile navigation click events by closing the sidebar
+ */
+ onMobileNavClick(): void {
+ // Only close sidebar on mobile view
+ if (window.innerWidth <= 768) {
+ this.mobileSidebarVisible.set(false);
+ }
+ }
+
/**
* Get the current page title based on the active route
*/
@@ -73,10 +86,16 @@ export class MainLayoutComponent {
}
}
+ /**
+ * Toggle mobile sidebar visibility
+ */
toggleMobileSidebar(): void {
this.mobileSidebarVisible.update(value => !value);
}
+ /**
+ * Toggle desktop sidebar collapsed state
+ */
toggleSidebar(): void {
this.isSidebarCollapsed.update(value => !value);
}
diff --git a/code/UI/src/app/layout/sidebar-content/sidebar-content.component.html b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.html
new file mode 100644
index 00000000..e2c7d6ad
--- /dev/null
+++ b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
Cleanuparr
+
+
+
+
diff --git a/code/UI/src/app/layout/sidebar-content/sidebar-content.component.scss b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.scss
new file mode 100644
index 00000000..19d60014
--- /dev/null
+++ b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.scss
@@ -0,0 +1,177 @@
+// Logo container
+.logo-container {
+ display: flex;
+ align-items: center;
+ padding: 15px 20px;
+ margin-bottom: 5px;
+
+ .logo {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ margin-right: 15px;
+ background-color: rgba(142, 68, 173, 0.1);
+ border: 1px solid rgba(142, 68, 173, 0.3);
+ animation: logo-glow 10s infinite ease-in-out;
+
+ i {
+ font-size: 20px;
+ color: #a569bd;
+ }
+ }
+
+ .logo-small {
+ display: none;
+ }
+
+ h1 {
+ font-size: 20px;
+ font-weight: 700;
+ margin: 0;
+ background: linear-gradient(to right, #fff, #bbb);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+}
+
+// Navigation menu
+.nav-menu {
+ display: flex;
+ flex-direction: column;
+ padding: 5px 0 20px 0;
+ flex: 1;
+
+ // Sponsor link
+ .sponsor-link {
+ margin-bottom: 15px;
+ border-bottom: 1px solid rgba(142, 68, 173, 0.15);
+ padding-bottom: 15px !important;
+
+ .heart-icon i {
+ color: #ff4d6d !important;
+ animation: heart-pulse 7.2s infinite ease-in-out;
+ }
+ }
+
+ // Nav groups
+ .nav-group {
+ margin-bottom: 15px;
+
+ .nav-group-title {
+ font-size: 12px;
+ font-weight: 700;
+ color: rgba(142, 68, 173, 0.8);
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ padding: 0 20px 8px;
+ margin-top: 10px;
+ margin-bottom: 5px;
+ border-bottom: 1px solid rgba(142, 68, 173, 0.2);
+ text-shadow: 0 0 10px rgba(142, 68, 173, 0.3);
+ }
+ }
+
+ // Navigation items
+ .nav-item {
+ display: flex;
+ align-items: center;
+ padding: 10px 20px;
+ color: #e0e0e0;
+ text-decoration: none;
+ border-radius: 0 6px 6px 0;
+ margin: 2px 0 2px 0;
+ position: relative;
+ overflow: hidden;
+ transition: all 0.2s ease;
+
+ .nav-icon-wrapper {
+ width: 32px;
+ height: 32px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 15px;
+ border-radius: 8px;
+ background: rgba(46, 39, 56, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ transition: all 0.2s ease;
+
+ i {
+ font-size: 14px;
+ color: #e0e0e0;
+ }
+ }
+
+ span {
+ white-space: nowrap;
+ font-size: 14px;
+ }
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 3px;
+ background: transparent;
+ transition: all 0.2s ease;
+ }
+
+ &:hover {
+ background: rgba(142, 68, 173, 0.05);
+
+ .nav-icon-wrapper {
+ background: rgba(142, 68, 173, 0.2);
+ transform: scale(1.05);
+ }
+
+ &::before {
+ background: linear-gradient(to bottom, rgba(142, 68, 173, 0.4), rgba(103, 58, 183, 0.4));
+ }
+ }
+
+ &.active {
+ background: linear-gradient(to right, rgba(142, 68, 173, 0.9), rgba(103, 58, 183, 0.7));
+ box-shadow: 0 2px 10px rgba(142, 68, 173, 0.3);
+
+ .nav-icon-wrapper {
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.2);
+ }
+
+ &::before {
+ background: #fff;
+ }
+ }
+ }
+}
+
+// Animation keyframes
+@keyframes logo-glow {
+ 0% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Purple
+ 20% { box-shadow: 0 0 15px #9b59b6, 0 0 5px rgba(155, 89, 182, 0.4); } // Lighter purple
+ 40% { box-shadow: 0 0 15px #673ab7, 0 0 5px rgba(103, 58, 183, 0.4); } // Deep purple
+ 60% { box-shadow: 0 0 15px #5e35b1, 0 0 5px rgba(94, 53, 177, 0.4); } // Dark deep purple
+ 80% { box-shadow: 0 0 15px #7e57c2, 0 0 5px rgba(126, 87, 194, 0.4); } // Medium purple
+ 100% { box-shadow: 0 0 15px #8e44ad, 0 0 5px rgba(142, 68, 173, 0.4); } // Back to purple
+}
+
+@keyframes heart-pulse {
+ 0% {
+ transform: scale(1);
+ opacity: 0.7;
+ }
+ 50% {
+ transform: scale(1.2);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 0.7;
+ }
+}
diff --git a/code/UI/src/app/layout/sidebar-content/sidebar-content.component.ts b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.ts
new file mode 100644
index 00000000..f444f6de
--- /dev/null
+++ b/code/UI/src/app/layout/sidebar-content/sidebar-content.component.ts
@@ -0,0 +1,40 @@
+import { Component, Input, inject, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, RouterLink } from '@angular/router';
+import { ButtonModule } from 'primeng/button';
+
+interface MenuItem {
+ label: string;
+ icon: string;
+ route: string;
+ badge?: string;
+}
+
+@Component({
+ selector: 'app-sidebar-content',
+ standalone: true,
+ imports: [
+ CommonModule,
+ RouterLink,
+ ButtonModule
+ ],
+ templateUrl: './sidebar-content.component.html',
+ styleUrl: './sidebar-content.component.scss'
+})
+export class SidebarContentComponent {
+ @Input() menuItems: MenuItem[] = [];
+ @Input() isMobile = false;
+ @Output() navItemClicked = new EventEmitter();
+
+ // Inject router for active route styling
+ public router = inject(Router);
+
+ /**
+ * Handle navigation item click
+ */
+ onNavItemClick(): void {
+ if (this.isMobile) {
+ this.navItemClicked.emit();
+ }
+ }
+}