diff --git a/public/icons/favorite.svg b/public/icons/favorite.svg new file mode 100644 index 0000000..4f93eab --- /dev/null +++ b/public/icons/favorite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/placeholder.svg b/public/icons/placeholder.svg new file mode 100644 index 0000000..821a6c5 --- /dev/null +++ b/public/icons/placeholder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/features/demo/components/demo2/demo2.component.html b/src/app/features/demo/components/demo2/demo2.component.html index 96c6974..775c928 100644 --- a/src/app/features/demo/components/demo2/demo2.component.html +++ b/src/app/features/demo/components/demo2/demo2.component.html @@ -6,81 +6,8 @@ - + @@ -130,83 +57,11 @@

Letzte Bestellungen

-
- - - - - - - - - - - - - - - - - - - -
- Kunde - - - Bestell-IDStatusBetragAktionen
-
- Avatar Max Mustermann -
-
Max Mustermann
-
- max.mustermann@example.com -
-
-
-
#10543 - Abgeschlossen - € 129,99 - - - - - -
-
+ Buttons, Chips & Interaktion
- Primary - Secondary - Stroked - Flat - ... + Primary + Secondary + Stroked + Flat +
+ +
- +

Indikatoren & Feedback

- Snackbar anzeigen Sind Sie sicher? Diese Aktion kann nicht rückgängig gemacht werden.

- Abbrechen - Abbrechen + Ja, endgültig löschen
diff --git a/src/app/features/demo/components/demo2/demo2.component.ts b/src/app/features/demo/components/demo2/demo2.component.ts index a63aaff..8dc2d71 100644 --- a/src/app/features/demo/components/demo2/demo2.component.ts +++ b/src/app/features/demo/components/demo2/demo2.component.ts @@ -12,11 +12,17 @@ import { PageHeaderComponent } from '../../../../shared/components/layout/page-h import { SlideToggleComponent } from '../../../../shared/components/form/slide-toggle/slide-toggle.component'; import { StatusPillComponent } from '../../../../shared/components/ui/status-pill/status-pill.component'; import { DialogComponent } from '../../../../shared/components/overlays/dialog/dialog.component'; -import { MenuComponent } from '../../../../shared/components/overlays/menu/menu.component'; +import { + MenuComponent, + MenuItem, +} from '../../../../shared/components/overlays/menu/menu.component'; import { AlertComponent } from '../../../../shared/components/ui/alert/alert.component'; import { PaginatorComponent } from '../../../../shared/components/data-display/paginator/paginator.component'; import { SkeletonComponent } from '../../../../shared/components/ui/skeleton/skeleton.component'; import { ExpansionPanelComponent } from '../../../../shared/components/layout/expansion-panel/expansion-panel.component'; +import { SidebarComponent } from '../../../../shared/components/layout/sidebar/sidebar.component'; + +import { OrdersTableComponent, Order } from '../../../../shared/components/data-display/orders-table/orders-table.component'; @Component({ selector: 'app-demo2', @@ -37,6 +43,8 @@ import { ExpansionPanelComponent } from '../../../../shared/components/layout/ex PaginatorComponent, SkeletonComponent, ExpansionPanelComponent, + SidebarComponent, + OrdersTableComponent ], templateUrl: './demo2.component.html', styleUrl: './demo2.component.css', @@ -45,13 +53,54 @@ export class Demo2Component implements OnInit { // Zustands-Variablen für diese Demo-Seite isLoading = true; isDialogOpen = false; - isComponentMenuOpen = false; + // isComponentMenuOpen = false; // <--- DIESE ZEILE ENTFERNEN, DA SIE NICHT MEHR BENÖTIGT WIRD // Eigenschaften für den Paginator currentPage = 1; totalItems = 123; itemsPerPage = 10; + actionMenuItems: MenuItem[] = [ + { + label: 'Bearbeiten', + action: () => this.snackbarService.show('Bearbeiten geklickt!'), + }, + { + label: 'Kopieren', + action: () => console.log('Kopieren geklickt!'), + }, + { + dividerBefore: true, + label: 'Löschen', + action: () => this.openDialog(), // Öffnet z.B. den Bestätigungs-Dialog + isDanger: true, + }, + ]; + + ordersData: Order[] = [ + { + id: '10543', + user: { name: 'Max Mustermann', email: 'max.mustermann@example.com', avatarUrl: 'https://i.pravatar.cc/40?u=max' }, + amount: '€ 129,99', + status: 'success', + statusText: 'Abgeschlossen' + }, + { + id: '10542', + user: { name: 'Erika Mustermann', email: 'erika.m@example.com', avatarUrl: 'https://i.pravatar.cc/40?u=erika' }, + amount: '€ 49,50', + status: 'warning', + statusText: 'In Bearbeitung' + }, + { + id: '10541', + user: { name: 'Peter Pan', email: 'peter.pan@example.com', avatarUrl: 'https://i.pravatar.cc/40?u=peter' }, + amount: '€ 87,00', + status: 'danger', + statusText: 'Storniert' + } + ]; + constructor(private snackbarService: SnackbarService) {} ngOnInit(): void { @@ -80,4 +129,9 @@ export class Demo2Component implements OnInit { console.log('Wechsle zu Seite:', newPage); // Hier würde die Logik zum Neuladen der Tabellendaten folgen } + + handleDeleteOrder(orderId: string) { + console.log('Lösche Bestellung mit ID:', orderId); + this.openDialog(); // Öffnet z.B. den globalen Dialog + } } diff --git a/src/app/shared/components/data-display/orders-table/orders-table.component.css b/src/app/shared/components/data-display/orders-table/orders-table.component.css new file mode 100644 index 0000000..2cfcb8a --- /dev/null +++ b/src/app/shared/components/data-display/orders-table/orders-table.component.css @@ -0,0 +1,85 @@ +/* Verschieben Sie alle Tabellen-Stile aus styles.css hierher */ +:host { + display: block; +} + +.table-container { + width: 100%; + overflow-x: auto; +} +.modern-table { + width: 100%; + border-collapse: collapse; + white-space: nowrap; +} +.modern-table thead th { + padding: 0.75rem 1.5rem; + color: var(--color-text-light); + font-size: 0.85rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + text-align: left; + border-bottom: 2px solid var(--color-border); +} +.modern-table th.sortable { + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 0.5rem; +} +.modern-table th.sortable:hover { + color: var(--color-text); +} +.modern-table .sort-icon { + opacity: 0.5; + transition: all var(--transition-speed); +} +.modern-table th.sortable:hover .sort-icon { + opacity: 1; +} +.modern-table tbody tr { + transition: background-color var(--transition-speed); + border-bottom: 1px solid var(--color-border); +} +.modern-table tbody tr:last-of-type { + border-bottom: none; +} +.modern-table tbody tr:hover { + background-color: var(--color-body-bg); +} +.modern-table tbody td { + padding: 1rem 1.5rem; + vertical-align: middle; +} +.user-cell { + display: flex; + align-items: center; + gap: 1rem; +} +.user-cell img { + width: 40px; + height: 40px; + border-radius: 50%; + object-fit: cover; +} +.user-name { + font-weight: 600; + color: var(--color-text); +} +.user-email { + font-size: 0.9rem; + color: var(--color-text-light); +} +.amount { font-weight: 600; } +.mono { font-family: 'Courier New', Courier, monospace; } +.actions-cell { + display: flex; + justify-content: center; + gap: 0.5rem; +} +.no-data-cell { + text-align: center; + padding: 2rem; + color: var(--color-text-light); +} \ No newline at end of file diff --git a/src/app/shared/components/data-display/orders-table/orders-table.component.html b/src/app/shared/components/data-display/orders-table/orders-table.component.html new file mode 100644 index 0000000..9ff911d --- /dev/null +++ b/src/app/shared/components/data-display/orders-table/orders-table.component.html @@ -0,0 +1,59 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ Kunde + + Bestell-IDStatusBetragAktionen
+
+ +
+
{{ order.user.name }}
+
{{ order.user.email }}
+
+
+
#{{ order.id }} + + {{ order.amount }} + + + + + + +
Keine Bestellungen gefunden.
+
\ No newline at end of file diff --git a/src/app/shared/components/data-display/orders-table/orders-table.component.ts b/src/app/shared/components/data-display/orders-table/orders-table.component.ts new file mode 100644 index 0000000..c0d6d59 --- /dev/null +++ b/src/app/shared/components/data-display/orders-table/orders-table.component.ts @@ -0,0 +1,42 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StatusPillComponent } from '../../ui/status-pill/status-pill.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { IconComponent } from '../../ui/icon/icon.component'; + +// Interfaces für die Datenstruktur +export interface OrderUser { + name: string; + email: string; + avatarUrl: string; +} + +export interface Order { + id: string; + user: OrderUser; + amount: string; + status: 'success' | 'warning' | 'danger'; + statusText: string; +} + +@Component({ + selector: 'app-orders-table', + standalone: true, + imports: [ + CommonModule, + + ButtonComponent, + IconComponent + ], + templateUrl: './orders-table.component.html', + styleUrl: './orders-table.component.css' +}) +export class OrdersTableComponent { + // Nimmt die anzuzeigenden Bestelldaten entgegen + @Input() orders: Order[] = []; + + // Gibt Events für die Aktionen aus, mit der ID der betroffenen Bestellung + @Output() viewDetails = new EventEmitter(); + @Output() editOrder = new EventEmitter(); + @Output() deleteOrder = new EventEmitter(); +} \ No newline at end of file diff --git a/src/app/shared/components/layout/sidebar/sidebar.component.css b/src/app/shared/components/layout/sidebar/sidebar.component.css new file mode 100644 index 0000000..1d9f8e6 --- /dev/null +++ b/src/app/shared/components/layout/sidebar/sidebar.component.css @@ -0,0 +1,66 @@ +/* Verschieben Sie alle .sidebar... und .nav-item... Klassen aus styles.css hierher */ +:host { + display: block; +} + +.sidebar { + width: 260px; + height: 100vh; + position: sticky; + top: 0; + display: flex; + flex-direction: column; + padding: 1.5rem; + background: var(--glass-bg); + backdrop-filter: var(--glass-backdrop-filter); + -webkit-backdrop-filter: var(--glass-backdrop-filter); + border-right: 1px solid var(--color-border); + transition: background-color var(--transition-speed), + border-color var(--transition-speed); +} +.sidebar-header { + margin-bottom: 2rem; +} +.sidebar-title { + font-size: 1.5rem; + font-weight: 900; +} +.sidebar-nav { + display: flex; + flex-direction: column; + gap: 0.5rem; + flex-grow: 1; +} +.sidebar-footer { + margin-top: auto; +} +.nav-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.75rem 1rem; + border-radius: var(--border-radius-md); + color: var(--color-text-light); + font-weight: 500; + text-decoration: none; + transition: all var(--transition-speed); +} +.nav-item app-icon { + font-size: 20px; /* Steuert die Größe des Icons */ + transition: color var(--transition-speed); +} +.nav-item:hover { + background-color: rgba(0, 0, 0, 0.05); + color: var(--color-text); +} +:host-context(body.dark-theme) .nav-item:hover { + background-color: rgba(255, 255, 255, 0.05); +} +.nav-item.active { + background: var(--color-primary-gradient); + color: #fff; + box-shadow: 0 4px 10px rgba(52, 152, 219, 0.4); +} +.nav-item.active app-icon { + color: #fff; +} \ No newline at end of file diff --git a/src/app/shared/components/layout/sidebar/sidebar.component.html b/src/app/shared/components/layout/sidebar/sidebar.component.html new file mode 100644 index 0000000..5c36dee --- /dev/null +++ b/src/app/shared/components/layout/sidebar/sidebar.component.html @@ -0,0 +1,45 @@ + diff --git a/src/app/shared/components/layout/sidebar/sidebar.component.ts b/src/app/shared/components/layout/sidebar/sidebar.component.ts new file mode 100644 index 0000000..fc371bf --- /dev/null +++ b/src/app/shared/components/layout/sidebar/sidebar.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { IconComponent } from '../../ui/icon/icon.component'; + +@Component({ + selector: 'app-sidebar', + standalone: true, + imports: [CommonModule, IconComponent], + templateUrl: './sidebar.component.html', + styleUrl: './sidebar.component.css' +}) +export class SidebarComponent { + // Wir verwalten nur noch, welcher Link aktiv ist. 'dashboard' ist der Standard. + activeRoute = 'dashboard'; + + // Methode, um den aktiven Link bei einem Klick zu ändern. + // In einer echten App würde dies durch den Angular Router gesteuert werden. + setActive(route: string): void { + this.activeRoute = route; + } +} \ No newline at end of file diff --git a/src/app/shared/components/overlays/menu/menu.component.css b/src/app/shared/components/overlays/menu/menu.component.css index 5dcaa18..34fdf02 100644 --- a/src/app/shared/components/overlays/menu/menu.component.css +++ b/src/app/shared/components/overlays/menu/menu.component.css @@ -1,31 +1,46 @@ -/* Verschieben Sie diese Stile aus styles.css hierher */ :host { + position: relative; /* Wichtig, damit das Panel sich daran ausrichtet */ display: inline-block; - position: relative; +} + +.menu-trigger { + cursor: pointer; + display: inline-block; +} + +/* Der unsichtbare Hintergrund, der die ganze Seite abdeckt */ +.menu-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 899; /* Direkt unter dem Menü-Panel */ + background: transparent; } .menu-panel { position: absolute; - top: calc(100% + 8px); - right: 0; + top: calc(100% + 8px); /* Position direkt unter dem Trigger */ min-width: 200px; padding: 0.5rem 0; - z-index: 900; + z-index: 900; /* Über dem Backdrop */ border: 1px solid var(--color-border); - opacity: 0; - visibility: hidden; - transform: translateY(-10px); - transition: opacity var(--transition-speed), transform var(--transition-speed); + box-shadow: var(--box-shadow-md); } -:host-context(.open) .menu-panel { - opacity: 1; - visibility: visible; - transform: translateY(0); +/* Ausrichtung des Panels */ +.menu-panel.align-right { + right: 0; + left: auto; +} +.menu-panel.align-left { + left: 0; + right: auto; } -/* Wir verwenden ::ng-deep, um die Stile auf die von außen projizierten Elemente anzuwenden */ -::ng-deep .menu-item { +/* Stile für die Menü-Items */ +.menu-item { display: block; padding: 0.6rem 1.25rem; color: var(--color-text); @@ -34,11 +49,14 @@ transition: background-color 0.2s ease-out; cursor: pointer; } - -::ng-deep .menu-item:hover { +.menu-item:hover { background-color: var(--color-body-bg); } - -::ng-deep .menu-item-danger { +.menu-item-danger { color: var(--color-danger); +} +.divider { + border: none; + border-top: 1px solid var(--color-border); + margin: 0.5rem 0; } \ No newline at end of file diff --git a/src/app/shared/components/overlays/menu/menu.component.html b/src/app/shared/components/overlays/menu/menu.component.html index 0c192a8..589d627 100644 --- a/src/app/shared/components/overlays/menu/menu.component.html +++ b/src/app/shared/components/overlays/menu/menu.component.html @@ -1,11 +1,26 @@ -