Initial commit: sap-intercompany-viewer
Frontend Angular 22 (standalone, signals, Material 3) para sap-intercompany-api. Pantallas: log de eventos/intentos, CRUD de databases, listado/alta/detalle de scenarios. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
commit
cf793c88f2
57 files changed
+10675
No files matched your search
@@ -0,0 +1,51 @@
|
||||
.tiles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background: var(--mat-sys-surface-container-low, rgba(0, 0, 0, 0.03));
|
||||
border-left: 4px solid #757575;
|
||||
}
|
||||
|
||||
.tile mat-icon {
|
||||
font-size: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.tile-value {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.tile-label {
|
||||
opacity: 0.7;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.tile-success {
|
||||
border-left-color: #2e7d32;
|
||||
}
|
||||
|
||||
.tile-success mat-icon {
|
||||
color: #2e7d32;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tile-danger {
|
||||
border-left-color: #c62828;
|
||||
}
|
||||
|
||||
.tile-danger mat-icon {
|
||||
color: #c62828;
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<h1>Dashboard</h1>
|
||||
|
||||
@if (loading()) {
|
||||
<mat-spinner diameter="32" />
|
||||
} @else {
|
||||
<div class="tiles">
|
||||
@for (tile of tiles(); track tile.label) {
|
||||
<div class="tile" [class.tile-success]="tile.tone === 'success'" [class.tile-danger]="tile.tone === 'danger'">
|
||||
<mat-icon>{{ tile.icon }}</mat-icon>
|
||||
<div class="tile-body">
|
||||
<div class="tile-value">{{ tile.value }}</div>
|
||||
<div class="tile-label">{{ tile.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Component, computed, inject, OnInit, signal } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Dashboard } from '../dashboard';
|
||||
import { DashboardStats } from '../../core/models';
|
||||
|
||||
type TileTone = 'neutral' | 'success' | 'danger';
|
||||
|
||||
interface Tile {
|
||||
label: string;
|
||||
value: number;
|
||||
icon: string;
|
||||
tone: TileTone;
|
||||
}
|
||||
|
||||
@Component({
|
||||
imports: [MatIconModule, MatProgressSpinnerModule],
|
||||
selector: 'app-dashboard-page',
|
||||
styleUrl: './dashboard-page.css',
|
||||
templateUrl: './dashboard-page.html',
|
||||
})
|
||||
export class DashboardPage implements OnInit {
|
||||
private readonly dashboardApi = inject(Dashboard);
|
||||
private readonly snackBar = inject(MatSnackBar);
|
||||
|
||||
protected readonly loading = signal(true);
|
||||
protected readonly stats = signal<DashboardStats | null>(null);
|
||||
|
||||
protected readonly tiles = computed<Tile[]>(() => {
|
||||
const s = this.stats();
|
||||
if (!s) return [];
|
||||
return [
|
||||
{ label: 'Escenarios activos', value: s.activeScenarios, icon: 'schema', tone: 'neutral' },
|
||||
{ label: 'Bases registradas', value: s.totalDatabases, icon: 'storage', tone: 'neutral' },
|
||||
{ label: 'Mensajes sin procesar', value: s.pendingEvents, icon: 'inbox', tone: 'neutral' },
|
||||
{ label: 'Procesados hoy', value: s.doneToday, icon: 'check_circle', tone: 'success' },
|
||||
{ label: 'Fallas hoy', value: s.failedToday, icon: 'error', tone: 'danger' },
|
||||
{ label: 'Procesados (últimos 7 días)', value: s.doneLast7Days, icon: 'check_circle', tone: 'success' },
|
||||
{ label: 'Fallas (últimos 7 días)', value: s.failedLast7Days, icon: 'error', tone: 'danger' },
|
||||
];
|
||||
});
|
||||
|
||||
ngOnInit() {
|
||||
this.dashboardApi.getStats().subscribe({
|
||||
next: (stats) => {
|
||||
this.stats.set(stats);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
this.snackBar.open('No se pudieron cargar los indicadores', 'Cerrar', { duration: 4000 });
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Service, inject } from '@angular/core';
|
||||
import { Api } from '../core/api';
|
||||
import { DashboardStats } from '../core/models';
|
||||
|
||||
@Service()
|
||||
export class Dashboard {
|
||||
private readonly api = inject(Api);
|
||||
|
||||
getStats() {
|
||||
return this.api.get<DashboardStats>('/dashboard/stats');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user