autocomit

This commit is contained in:
2026-02-08 16:44:04 +01:00
parent 1aeadb9c99
commit c6a514718c
8 changed files with 635 additions and 485 deletions
+18
View File
@@ -0,0 +1,18 @@
# Feature 1
Dans l'écran applications, je veux pouvoir ajouter des contacts à l'application.
# Feature 2
J'aimerais un écran me permettant de gérer les utilisateurs de l'application.
(ou de permettre d'utiliser un LDAP pour identifier les users)
A ce moment il faudra pouvoir assigner les users aux business units pour qu'ils puissent gérer les entités relatives à cette business unit la.
admin peut tout faire.
# Feature 3
J'aimerais que les badges "quick access" soient toujours disponible dans une bannière à gauche sur chaque écran.
Aujourd'hui les écrans de gestion (business units, applications, environments, personnes ...) sont des pages uniques et ce n'est pas pratique.
Je préfèrerais un dashboard cohérent qui me permette d'accéder à tous les éléments de l'application en gardant ce menu à gauche.
est-ce clair ? (n'implémente pas encore)
+123 -123
View File
@@ -12,139 +12,139 @@ export const routes: Routes = [
loadComponent: () => import('./core/auth/login/login.component').then(m => m.LoginComponent)
},
{
path: 'dashboard',
canActivate: [authGuard],
loadComponent: () => import('./features/dashboard/dashboard.component').then(m => m.DashboardComponent)
},
{
path: 'business-units',
path: '',
canActivate: [authGuard],
loadComponent: () => import('./core/layout/main-layout/main-layout.component').then(m => m.MainLayoutComponent),
children: [
{
path: '',
loadComponent: () => import('./features/business-units/business-unit-list/business-unit-list.component')
.then(m => m.BusinessUnitListComponent)
path: 'dashboard',
loadComponent: () => import('./features/dashboard/dashboard.component').then(m => m.DashboardComponent)
},
{
path: 'new',
loadComponent: () => import('./features/business-units/business-unit-form/business-unit-form.component')
.then(m => m.BusinessUnitFormComponent)
path: 'business-units',
children: [
{
path: '',
loadComponent: () => import('./features/business-units/business-unit-list/business-unit-list.component')
.then(m => m.BusinessUnitListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/business-units/business-unit-form/business-unit-form.component')
.then(m => m.BusinessUnitFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/business-units/business-unit-detail/business-unit-detail.component')
.then(m => m.BusinessUnitDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/business-units/business-unit-form/business-unit-form.component')
.then(m => m.BusinessUnitFormComponent)
}
]
},
{
path: ':id',
loadComponent: () => import('./features/business-units/business-unit-detail/business-unit-detail.component')
.then(m => m.BusinessUnitDetailComponent)
path: 'applications',
children: [
{
path: '',
loadComponent: () => import('./features/applications/application-list/application-list.component')
.then(m => m.ApplicationListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/applications/application-form/application-form.component')
.then(m => m.ApplicationFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/applications/application-detail/application-detail.component')
.then(m => m.ApplicationDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/applications/application-form/application-form.component')
.then(m => m.ApplicationFormComponent)
}
]
},
{
path: ':id/edit',
loadComponent: () => import('./features/business-units/business-unit-form/business-unit-form.component')
.then(m => m.BusinessUnitFormComponent)
path: 'environments',
children: [
{
path: '',
loadComponent: () => import('./features/environments/environment-list/environment-list.component')
.then(m => m.EnvironmentListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/environments/environment-form/environment-form.component')
.then(m => m.EnvironmentFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/environments/environment-detail/environment-detail.component')
.then(m => m.EnvironmentDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/environments/environment-form/environment-form.component')
.then(m => m.EnvironmentFormComponent)
}
]
},
{
path: 'persons',
children: [
{
path: '',
loadComponent: () => import('./features/persons/person-list/person-list.component')
.then(m => m.PersonListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/persons/person-form/person-form.component')
.then(m => m.PersonFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/persons/person-detail/person-detail.component')
.then(m => m.PersonDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/persons/person-form/person-form.component')
.then(m => m.PersonFormComponent)
}
]
},
{
path: 'contacts',
children: [
{
path: '',
loadComponent: () => import('./features/contacts/contact-list/contact-list.component')
.then(m => m.ContactListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/contacts/contact-form/contact-form.component')
.then(m => m.ContactFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/contacts/contact-detail/contact-detail.component')
.then(m => m.ContactDetailComponent)
}
]
},
{
path: 'contact-roles',
loadComponent: () => import('./features/contacts/contact-role-list/contact-role-list.component')
.then(m => m.ContactRoleListComponent)
}
]
},
{
path: 'applications',
canActivate: [authGuard],
children: [
{
path: '',
loadComponent: () => import('./features/applications/application-list/application-list.component')
.then(m => m.ApplicationListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/applications/application-form/application-form.component')
.then(m => m.ApplicationFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/applications/application-detail/application-detail.component')
.then(m => m.ApplicationDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/applications/application-form/application-form.component')
.then(m => m.ApplicationFormComponent)
}
]
},
{
path: 'environments',
canActivate: [authGuard],
children: [
{
path: '',
loadComponent: () => import('./features/environments/environment-list/environment-list.component')
.then(m => m.EnvironmentListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/environments/environment-form/environment-form.component')
.then(m => m.EnvironmentFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/environments/environment-detail/environment-detail.component')
.then(m => m.EnvironmentDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/environments/environment-form/environment-form.component')
.then(m => m.EnvironmentFormComponent)
}
]
},
{
path: 'persons',
canActivate: [authGuard],
children: [
{
path: '',
loadComponent: () => import('./features/persons/person-list/person-list.component')
.then(m => m.PersonListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/persons/person-form/person-form.component')
.then(m => m.PersonFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/persons/person-detail/person-detail.component')
.then(m => m.PersonDetailComponent)
},
{
path: ':id/edit',
loadComponent: () => import('./features/persons/person-form/person-form.component')
.then(m => m.PersonFormComponent)
}
]
},
{
path: 'contacts',
canActivate: [authGuard],
children: [
{
path: '',
loadComponent: () => import('./features/contacts/contact-list/contact-list.component')
.then(m => m.ContactListComponent)
},
{
path: 'new',
loadComponent: () => import('./features/contacts/contact-form/contact-form.component')
.then(m => m.ContactFormComponent)
},
{
path: ':id',
loadComponent: () => import('./features/contacts/contact-detail/contact-detail.component')
.then(m => m.ContactDetailComponent)
}
]
},
{
path: 'contact-roles',
canActivate: [authGuard],
loadComponent: () => import('./features/contacts/contact-role-list/contact-role-list.component')
.then(m => m.ContactRoleListComponent)
}
];
@@ -0,0 +1,37 @@
<div class="main-layout">
<!-- Sidebar Navigation -->
<aside class="sidebar">
<div class="sidebar-header">
<h1>LDPv2</h1>
<p class="subtitle">Lifecycle Data Platform</p>
</div>
<nav class="sidebar-nav">
<div
*ngFor="let item of navItems"
class="nav-item"
[class.active]="isActive(item.route)"
[style.border-left-color]="item.color"
(click)="navigate(item.route)">
<span class="nav-icon">{{ item.icon }}</span>
<span class="nav-title">{{ item.title }}</span>
</div>
</nav>
<div class="sidebar-footer">
<div class="user-info" *ngIf="currentUser">
<span class="user-icon">👤</span>
<div class="user-details">
<div class="username">{{ currentUser.username }}</div>
<div class="role-badge">{{ currentUser.role }}</div>
</div>
</div>
<button (click)="logout()" class="btn-logout">Logout</button>
</div>
</aside>
<!-- Main Content Area -->
<main class="main-content">
<router-outlet></router-outlet>
</main>
</div>
@@ -0,0 +1,164 @@
.main-layout {
display: flex;
height: 100vh;
overflow: hidden;
}
.sidebar {
width: 260px;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
flex-direction: column;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.sidebar-header {
padding: 2rem 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
h1 {
margin: 0 0 0.25rem 0;
font-size: 1.75rem;
font-weight: 700;
}
.subtitle {
margin: 0;
font-size: 0.75rem;
opacity: 0.8;
}
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 1rem 0;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
}
&::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
&:hover {
background: rgba(255, 255, 255, 0.4);
}
}
}
.nav-item {
display: flex;
align-items: center;
padding: 1rem 1.5rem;
cursor: pointer;
transition: all 0.2s ease;
border-left: 4px solid transparent;
margin: 0.25rem 0;
&:hover {
background: rgba(255, 255, 255, 0.1);
}
&.active {
background: rgba(255, 255, 255, 0.2);
font-weight: 500;
}
.nav-icon {
font-size: 1.5rem;
margin-right: 1rem;
width: 30px;
text-align: center;
}
.nav-title {
font-size: 0.95rem;
}
}
.sidebar-footer {
padding: 1rem 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
.user-info {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
.user-icon {
font-size: 1.5rem;
}
.user-details {
flex: 1;
min-width: 0;
.username {
font-weight: 500;
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.role-badge {
font-size: 0.7rem;
opacity: 0.8;
text-transform: uppercase;
}
}
}
.btn-logout {
width: 100%;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 0.75rem;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
}
}
.main-content {
flex: 1;
overflow-y: auto;
background: #f5f5f5;
}
// Responsive
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: -260px;
height: 100vh;
transition: left 0.3s ease;
&.open {
left: 0;
}
}
.main-content {
width: 100%;
}
}
@@ -0,0 +1,102 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router, RouterModule, NavigationEnd } from '@angular/router';
import { AuthService } from '../../auth/auth.service';
import { User } from '../../../shared/models/user.model';
import { filter } from 'rxjs/operators';
interface NavItem {
title: string;
icon: string;
route: string;
color: string;
}
@Component({
selector: 'app-main-layout',
standalone: true,
imports: [CommonModule, RouterModule],
templateUrl: './main-layout.component.html',
styleUrls: ['./main-layout.component.scss']
})
export class MainLayoutComponent implements OnInit {
currentUser: User | null = null;
activeRoute: string = '';
navItems: NavItem[] = [
{
title: 'Dashboard',
icon: '📊',
route: '/dashboard',
color: '#2196f3'
},
{
title: 'Business Units',
icon: '🏢',
route: '/business-units',
color: '#3f51b5'
},
{
title: 'Applications',
icon: '📱',
route: '/applications',
color: '#009688'
},
{
title: 'Environments',
icon: '🌍',
route: '/environments',
color: '#ff9800'
},
{
title: 'Persons',
icon: '👤',
route: '/persons',
color: '#e91e63'
},
{
title: 'Contacts',
icon: '👥',
route: '/contacts',
color: '#9c27b0'
},
{
title: 'Contact Roles',
icon: '🎭',
route: '/contact-roles',
color: '#607d8b'
}
];
constructor(
private router: Router,
private authService: AuthService
) {}
ngOnInit(): void {
this.currentUser = this.authService.getCurrentUser();
this.activeRoute = this.router.url;
// Listen to route changes to update active route
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe((event: any) => {
this.activeRoute = event.url;
});
}
isActive(route: string): boolean {
if (route === '/dashboard') {
return this.activeRoute === '/dashboard';
}
return this.activeRoute.startsWith(route);
}
navigate(route: string): void {
this.router.navigate([route]);
}
logout(): void {
this.authService.logout();
}
}
@@ -1,95 +1,66 @@
<div class="dashboard">
<header class="dashboard-header">
<div class="container">
<div class="header-content">
<div class="branding">
<h1>LDPv2</h1>
<p class="subtitle">Lifecycle Data Platform</p>
</div>
<div class="user-menu">
<span class="user-info" *ngIf="currentUser">
<span class="user-icon">👤</span>
<span class="username">{{ currentUser.username }}</span>
<span class="role-badge">{{ currentUser.role }}</span>
</span>
<button (click)="logout()" class="btn-logout">Logout</button>
<div class="dashboard-content">
<div class="dashboard-header">
<h1>Welcome back, {{ currentUser?.username }}! 👋</h1>
<p>Here's an overview of your application lifecycle data.</p>
</div>
<section class="stats-section">
<h2>Overview</h2>
<div class="stats-grid">
<div class="stat-card" *ngFor="let stat of stats" [style.border-left-color]="stat.color">
<div class="stat-icon">{{ stat.icon }}</div>
<div class="stat-content">
<div class="stat-value">{{ stat.value }}</div>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
</div>
</header>
</section>
<main class="dashboard-main">
<div class="container">
<section class="welcome-section">
<h2>Welcome back, {{ currentUser?.username }}! 👋</h2>
<p>Manage your applications, environments, and business units from one place.</p>
</section>
<section class="stats-section">
<div class="stats-grid">
<div class="stat-card" *ngFor="let stat of stats">
<div class="stat-icon">{{ stat.icon }}</div>
<div class="stat-content">
<div class="stat-value">{{ stat.value }}</div>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
<section class="activity-section">
<h2>Recent Activity</h2>
<div class="activity-list">
<div class="activity-item" *ngFor="let activity of recentActivity">
<div class="activity-icon">
<span *ngIf="activity.action === 'Created'"></span>
<span *ngIf="activity.action === 'Updated'">📝</span>
<span *ngIf="activity.action === 'Deleted'">🗑️</span>
</div>
</section>
<section class="features-section">
<h3>Quick Access</h3>
<div class="features-grid">
<div
class="feature-card"
*ngFor="let feature of features"
(click)="navigate(feature.route)"
[style.border-left-color]="feature.color">
<div class="feature-icon">{{ feature.icon }}</div>
<div class="feature-content">
<h4>{{ feature.title }}</h4>
<p>{{ feature.description }}</p>
</div>
<div class="feature-arrow"></div>
<div class="activity-details">
<div class="activity-main">
<strong>{{ activity.action }}</strong> {{ activity.entity }}:
<span class="entity-name">{{ activity.name }}</span>
</div>
<div class="activity-time">{{ activity.time }}</div>
</div>
</section>
<section class="getting-started">
<h3>Getting Started</h3>
<div class="steps-grid">
<div class="step-card">
<div class="step-number">1</div>
<h4>Create Business Units</h4>
<p>Organize your applications by business units</p>
<button (click)="navigate('/business-units')" class="btn-link">
Go to Business Units →
</button>
</div>
<div class="step-card">
<div class="step-number">2</div>
<h4>Add Applications</h4>
<p>Register your applications and track their lifecycle</p>
<button (click)="navigate('/applications')" class="btn-link">
Go to Applications →
</button>
</div>
<div class="step-card">
<div class="step-number">3</div>
<h4>Configure Environments</h4>
<p>Set up deployment environments</p>
<button (click)="navigate('/environments')" class="btn-link">
Go to Environments →
</button>
</div>
</div>
</section>
</div>
</div>
</main>
</section>
<footer class="dashboard-footer">
<div class="container">
<p>LDPv2 - Lifecycle Data Platform v2 | © 2026</p>
<section class="quick-tips">
<h2>Quick Tips</h2>
<div class="tips-grid">
<div class="tip-card">
<div class="tip-icon">💡</div>
<div class="tip-content">
<h3>Get Started</h3>
<p>Use the sidebar to navigate between different sections of the application.</p>
</div>
</div>
<div class="tip-card">
<div class="tip-icon">🔍</div>
<div class="tip-content">
<h3>Search & Filter</h3>
<p>Most lists support search and filtering to help you find what you need quickly.</p>
</div>
</div>
<div class="tip-card">
<div class="tip-icon">🎯</div>
<div class="tip-content">
<h3>Track Lifecycle</h3>
<p>Monitor your applications' lifecycle from idea to decommission.</p>
</div>
</div>
</div>
</footer>
</section>
</div>
@@ -1,280 +1,184 @@
.dashboard {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
flex-direction: column;
}
.container {
max-width: 1200px;
.dashboard-content {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
padding: 0 2rem;
width: 100%;
}
.dashboard-header {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 1.5rem 0;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.branding h1 {
color: white;
h1 {
font-size: 2rem;
margin: 0;
font-weight: 700;
}
.branding .subtitle {
color: rgba(255, 255, 255, 0.8);
margin: 0;
font-size: 0.9rem;
}
.user-menu {
display: flex;
align-items: center;
gap: 1rem;
}
.user-info {
display: flex;
align-items: center;
gap: 0.5rem;
color: white;
.user-icon { font-size: 1.5rem; }
.username { font-weight: 500; }
.role-badge {
background: rgba(255, 255, 255, 0.2);
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.75rem;
text-transform: uppercase;
}
}
.btn-logout {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 0.5rem 1.5rem;
border-radius: 20px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
}
}
.dashboard-main {
flex: 1;
padding: 3rem 0;
}
.welcome-section {
text-align: center;
color: white;
margin-bottom: 3rem;
h2 {
font-size: 2.5rem;
margin: 0 0 1rem 0;
font-weight: 700;
margin: 0 0 0.5rem 0;
color: #333;
}
p {
font-size: 1.2rem;
opacity: 0.9;
margin: 0;
color: #666;
font-size: 1.1rem;
}
}
section {
margin-bottom: 3rem;
h2 {
font-size: 1.5rem;
margin: 0 0 1.5rem 0;
color: #333;
}
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.stat-card {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 1.5rem;
transition: transform 0.3s ease;
&:hover { transform: translateY(-5px); }
.stat-icon { font-size: 3rem; }
.stat-value {
font-size: 2.5rem;
font-weight: 700;
color: #333;
}
.stat-label {
color: #666;
font-size: 0.9rem;
}
}
.features-section {
margin-bottom: 3rem;
h3 {
color: white;
font-size: 1.8rem;
margin-bottom: 1.5rem;
font-weight: 600;
}
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
gap: 1rem;
border-left: 4px solid;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.feature-icon { font-size: 3rem; }
.feature-content {
.stat-icon {
font-size: 2.5rem;
}
.stat-content {
flex: 1;
h4 {
margin: 0 0 0.5rem 0;
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #333;
font-size: 1.3rem;
line-height: 1;
}
p {
margin: 0;
.stat-label {
color: #666;
font-size: 0.9rem;
margin-top: 0.25rem;
}
}
}
.feature-arrow {
font-size: 1.5rem;
color: #999;
.activity-section {
.activity-list {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
}
.getting-started {
h3 {
color: white;
font-size: 1.8rem;
margin-bottom: 1.5rem;
font-weight: 600;
}
}
.steps-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.step-card {
background: rgba(255, 255, 255, 0.95);
padding: 2rem;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
.step-number {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 50%;
.activity-item {
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.2rem;
margin-bottom: 1rem;
}
gap: 1rem;
padding: 1rem 1.5rem;
border-bottom: 1px solid #f5f5f5;
transition: background-color 0.2s ease;
h4 {
color: #333;
margin: 0 0 0.5rem 0;
font-size: 1.2rem;
}
p {
color: #666;
margin: 0 0 1rem 0;
}
.btn-link {
background: none;
border: none;
color: #667eea;
font-weight: 500;
cursor: pointer;
padding: 0;
transition: color 0.3s ease;
&:last-child {
border-bottom: none;
}
&:hover {
color: #764ba2;
text-decoration: underline;
background-color: #f9f9f9;
}
.activity-icon {
font-size: 1.5rem;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 50%;
}
.activity-details {
flex: 1;
.activity-main {
color: #333;
margin-bottom: 0.25rem;
.entity-name {
color: #667eea;
font-weight: 500;
}
}
.activity-time {
color: #999;
font-size: 0.85rem;
}
}
}
}
.dashboard-footer {
background: rgba(0, 0, 0, 0.2);
padding: 1.5rem 0;
text-align: center;
color: white;
margin-top: auto;
.quick-tips {
.tips-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
p {
margin: 0;
opacity: 0.8;
.tip-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
gap: 1rem;
.tip-icon {
font-size: 2rem;
}
.tip-content {
flex: 1;
h3 {
margin: 0 0 0.5rem 0;
color: #333;
font-size: 1.1rem;
}
p {
margin: 0;
color: #666;
font-size: 0.9rem;
line-height: 1.5;
}
}
}
}
@media (max-width: 768px) {
.dashboard-header .header-content {
flex-direction: column;
gap: 1rem;
.dashboard-content {
padding: 1rem;
}
.welcome-section h2 {
font-size: 1.8rem;
.dashboard-header h1 {
font-size: 1.5rem;
}
.stats-grid,
.features-grid,
.steps-grid {
.tips-grid {
grid-template-columns: 1fr;
}
}
@@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';
import { AuthService } from '../../core/auth/auth.service';
import { User } from '../../shared/models/user.model';
@@ -14,72 +13,27 @@ import { User } from '../../shared/models/user.model';
export class DashboardComponent implements OnInit {
currentUser: User | null = null;
features = [
{
title: 'Business Units',
description: 'Manage organizational business units',
icon: '🏢',
route: '/business-units',
color: '#3f51b5'
},
{
title: 'Applications',
description: 'Manage applications and their lifecycle',
icon: '📱',
route: '/applications',
color: '#009688'
},
{
title: 'Environments',
description: 'Manage deployment environments',
icon: '🌍',
route: '/environments',
color: '#ff9800'
},
{
title: 'Persons',
description: 'Manage individual contacts',
icon: '👤',
route: '/persons',
color: '#e91e63'
},
{
title: 'Contacts',
description: 'Manage functional contacts and roles',
icon: '👥',
route: '/contacts',
color: '#9c27b0'
},
{
title: 'Contact Roles',
description: 'View predefined contact roles',
icon: '🎭',
route: '/contact-roles',
color: '#607d8b'
}
stats = [
{ label: 'Business Units', value: '4', icon: '🏢', color: '#3f51b5' },
{ label: 'Applications', value: '7', icon: '📱', color: '#009688' },
{ label: 'Environments', value: '4', icon: '🌍', color: '#ff9800' },
{ label: 'Persons', value: '4', icon: '👤', color: '#e91e63' },
{ label: 'Contacts', value: '0', icon: '👥', color: '#9c27b0' },
{ label: 'Contact Roles', value: '8', icon: '🎭', color: '#607d8b' }
];
stats = [
{ label: 'Business Units', value: '4', icon: '🏢' },
{ label: 'Applications', value: '7', icon: '📱' },
{ label: 'Environments', value: '4', icon: '🌍' },
{ label: 'Persons', value: '4', icon: '👤' }
recentActivity = [
{ action: 'Created', entity: 'Application', name: 'Mobile App', time: '2 hours ago' },
{ action: 'Updated', entity: 'Business Unit', name: 'Digital Services', time: '5 hours ago' },
{ action: 'Created', entity: 'Person', name: 'John Doe', time: '1 day ago' },
{ action: 'Updated', entity: 'Environment', name: 'PROD-EU', time: '2 days ago' }
];
constructor(
private router: Router,
private authService: AuthService
) {}
ngOnInit(): void {
this.currentUser = this.authService.getCurrentUser();
}
navigate(route: string): void {
this.router.navigate([route]);
}
logout(): void {
this.authService.logout();
}
}