Renomeia Relatórios para Dashboard e adiciona menu de opções
This commit is contained in:
parent
e1744bb202
commit
c9c2f2fffb
|
|
@ -11,7 +11,7 @@ import { authGuard } from './guards/auth.guard';
|
|||
import { DadosUsuarios } from './pages/dados-usuarios/dados-usuarios';
|
||||
import { VigenciaComponent } from './pages/vigencia/vigencia';
|
||||
import { TrocaNumero } from './pages/troca-numero/troca-numero';
|
||||
import { Relatorios } from './pages/relatorios/relatorios';
|
||||
import { Dashboard } from './pages/dashboard/dashboard';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: Home },
|
||||
|
|
@ -26,10 +26,10 @@ export const routes: Routes = [
|
|||
{ path: 'trocanumero', component: TrocaNumero, canActivate: [authGuard] },
|
||||
|
||||
// ✅ rota correta
|
||||
{ path: 'relatorios', component: Relatorios, canActivate: [authGuard] },
|
||||
{ path: 'dashboard', component: Dashboard, canActivate: [authGuard] },
|
||||
|
||||
// ✅ compatibilidade: se alguém acessar /portal/relatorios, manda pra /relatorios
|
||||
{ path: 'portal/relatorios', redirectTo: 'relatorios', pathMatch: 'full' },
|
||||
// ✅ compatibilidade: se alguém acessar /portal/dashboard, manda pra /dashboard
|
||||
{ path: 'portal/dashboard', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
|
||||
{ path: '**', redirectTo: '' },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export class AppComponent {
|
|||
'/dadosusuarios',
|
||||
'/vigencia',
|
||||
'/trocanumero',
|
||||
'/relatorios', // ✅ ADICIONADO: esconde footer na página de relatórios
|
||||
'/dashboard', // ✅ ADICIONADO: esconde footer na página de dashboard
|
||||
];
|
||||
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -3,19 +3,54 @@
|
|||
|
||||
<!-- ✅ LOGADO: hambúrguer + logo JUNTOS -->
|
||||
<ng-container *ngIf="isLoggedHeader; else publicHeader">
|
||||
<div class="left-logged">
|
||||
<button class="btn-icon" type="button" (click)="toggleMenu()" aria-label="Abrir menu">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
<div class="logged-header">
|
||||
<div class="left-logged">
|
||||
<button class="btn-icon" type="button" (click)="toggleMenu()" aria-label="Abrir menu">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
|
||||
<a routerLink="/relatorios" class="logo-area" (click)="closeMenu()">
|
||||
<div class="logo-icon">
|
||||
<i class="bi bi-layers-fill"></i>
|
||||
<a routerLink="/dashboard" class="logo-area" (click)="closeMenu()">
|
||||
<div class="logo-icon">
|
||||
<i class="bi bi-layers-fill"></i>
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
Line<span class="highlight">Gestão</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="logged-actions">
|
||||
<span class="status-pill">
|
||||
<span class="status-dot"></span>
|
||||
LIVE
|
||||
</span>
|
||||
|
||||
<button type="button" class="btn-icon btn-bell" aria-label="Notificações">
|
||||
<i class="bi bi-bell"></i>
|
||||
</button>
|
||||
|
||||
<div class="options-menu" [class.open]="optionsOpen" (click)="$event.stopPropagation()">
|
||||
<button
|
||||
type="button"
|
||||
class="options-trigger"
|
||||
(click)="toggleOptions()"
|
||||
aria-haspopup="true"
|
||||
[attr.aria-expanded]="optionsOpen"
|
||||
>
|
||||
Opções
|
||||
<i class="bi bi-chevron-down"></i>
|
||||
</button>
|
||||
|
||||
<div class="options-dropdown" *ngIf="optionsOpen">
|
||||
<a routerLink="/dadosusuarios" class="options-item" (click)="closeOptions()">
|
||||
Perfil
|
||||
</a>
|
||||
<button type="button" class="options-item danger" (click)="logout()">
|
||||
Sair
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
Line<span class="highlight">Gestão</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
|
@ -58,7 +93,7 @@
|
|||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<div class="side-menu-header">
|
||||
<a class="side-logo" routerLink="/relatorios" (click)="closeMenu()">
|
||||
<a class="side-logo" routerLink="/dashboard" (click)="closeMenu()">
|
||||
<span class="side-logo-icon"><i class="bi bi-layers-fill"></i></span>
|
||||
<span class="side-logo-text">Line<span class="highlight">Gestão</span></span>
|
||||
</a>
|
||||
|
|
@ -69,8 +104,8 @@
|
|||
</div>
|
||||
|
||||
<div class="side-menu-body">
|
||||
<a routerLink="/relatorios" routerLinkActive="active" class="side-item" (click)="closeMenu()">
|
||||
<i class="bi bi-bar-chart-fill"></i> <span>Relatórios</span>
|
||||
<a routerLink="/dashboard" routerLinkActive="active" class="side-item" (click)="closeMenu()">
|
||||
<i class="bi bi-bar-chart-fill"></i> <span>Dashboard</span>
|
||||
</a>
|
||||
|
||||
<a routerLink="/geral" routerLinkActive="active" class="side-item" (click)="closeMenu()">
|
||||
|
|
|
|||
|
|
@ -30,6 +30,20 @@
|
|||
gap: 12px;
|
||||
}
|
||||
|
||||
.logged-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logged-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.logo-area {
|
||||
display: flex;
|
||||
|
|
@ -123,22 +137,99 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Faixa home */
|
||||
.header-bar {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
/* ✅ Status e opções (logado) */
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(90deg, #0B2BD6 0%, #6A55FF 40%, #E33DCF 100%);
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(16, 185, 129, 0.12);
|
||||
color: #0f766e;
|
||||
font-weight: 800;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.header-bar-text {
|
||||
color: #ffffff;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.18);
|
||||
}
|
||||
|
||||
.btn-bell {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.options-menu {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.options-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
background: #fff;
|
||||
font-weight: 700;
|
||||
color: var(--text-main);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(227, 61, 207, 0.35);
|
||||
box-shadow: 0 12px 22px rgba(0,0,0,0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.options-dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 8px);
|
||||
min-width: 200px;
|
||||
padding: 8px 0;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(0,0,0,0.08);
|
||||
background: #fff;
|
||||
box-shadow: 0 18px 40px rgba(0,0,0,0.12);
|
||||
z-index: 1200;
|
||||
}
|
||||
|
||||
.options-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
font-weight: 700;
|
||||
color: rgba(17, 18, 20, 0.85);
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(227, 61, 207, 0.08);
|
||||
}
|
||||
|
||||
&.danger {
|
||||
color: #c2410c;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================= */
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { RouterLink, Router, NavigationEnd } from '@angular/router';
|
|||
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
||||
import { PLATFORM_ID } from '@angular/core';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
|
|
@ -15,6 +16,7 @@ export class Header {
|
|||
isScrolled = false;
|
||||
|
||||
menuOpen = false;
|
||||
optionsOpen = false;
|
||||
isLoggedHeader = false;
|
||||
isHome = false;
|
||||
|
||||
|
|
@ -25,11 +27,12 @@ export class Header {
|
|||
'/dadosusuarios',
|
||||
'/vigencia',
|
||||
'/trocanumero',
|
||||
'/relatorios', // ✅ ADICIONADO
|
||||
'/dashboard', // ✅ ADICIONADO
|
||||
];
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private authService: AuthService,
|
||||
@Inject(PLATFORM_ID) private platformId: object
|
||||
) {
|
||||
// ✅ resolve no carregamento inicial
|
||||
|
|
@ -42,6 +45,7 @@ export class Header {
|
|||
const rawUrl = event.urlAfterRedirects || event.url;
|
||||
this.syncHeaderState(rawUrl);
|
||||
this.menuOpen = false;
|
||||
this.optionsOpen = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -63,15 +67,35 @@ export class Header {
|
|||
this.menuOpen = false;
|
||||
}
|
||||
|
||||
toggleOptions() {
|
||||
this.optionsOpen = !this.optionsOpen;
|
||||
}
|
||||
|
||||
closeOptions() {
|
||||
this.optionsOpen = false;
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.authService.logout();
|
||||
this.optionsOpen = false;
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
@HostListener('window:scroll', [])
|
||||
onWindowScroll() {
|
||||
if (!isPlatformBrowser(this.platformId)) return;
|
||||
this.isScrolled = window.scrollY > 10;
|
||||
}
|
||||
|
||||
@HostListener('document:click', [])
|
||||
onDocumentClick() {
|
||||
this.optionsOpen = false;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', [])
|
||||
onEsc() {
|
||||
if (!isPlatformBrowser(this.platformId)) return;
|
||||
this.closeMenu();
|
||||
this.closeOptions();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<section class="relatorios-page">
|
||||
<section class="dashboard-page">
|
||||
<div class="wrap">
|
||||
<div class="container">
|
||||
<div class="page-head fade-in-up">
|
||||
<div class="title">
|
||||
<span class="badge">
|
||||
<i class="bi bi-bar-chart-fill"></i> Relatórios
|
||||
<i class="bi bi-bar-chart-fill"></i> Dashboard
|
||||
</span>
|
||||
<p class="subtitle">Resumo e indicadores do ambiente.</p>
|
||||
</div>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
display: none !important;
|
||||
}
|
||||
|
||||
.relatorios-page {
|
||||
.dashboard-page {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ type DashboardKpisDto = {
|
|||
userDataComEmail: number;
|
||||
};
|
||||
|
||||
type RelatoriosDashboardDto = {
|
||||
type DashboardDto = {
|
||||
kpis: DashboardKpisDto;
|
||||
|
||||
topClientes: TopClienteDto[];
|
||||
|
|
@ -105,13 +105,13 @@ type RelatoriosDashboardDto = {
|
|||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-relatorios',
|
||||
selector: 'app-dashboard',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './relatorios.html',
|
||||
styleUrls: ['./relatorios.scss'],
|
||||
templateUrl: './dashboard.html',
|
||||
styleUrls: ['./dashboard.scss'],
|
||||
})
|
||||
export class Relatorios implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class Dashboard implements OnInit, AfterViewInit, OnDestroy {
|
||||
@ViewChild('chartMureg12') chartMureg12?: ElementRef<HTMLCanvasElement>;
|
||||
@ViewChild('chartTroca12') chartTroca12?: ElementRef<HTMLCanvasElement>;
|
||||
@ViewChild('chartStatusPie') chartStatusPie?: ElementRef<HTMLCanvasElement>;
|
||||
|
|
@ -202,17 +202,17 @@ export class Relatorios implements OnInit, AfterViewInit, OnDestroy {
|
|||
} catch {
|
||||
this.loading = false;
|
||||
this.errorMsg =
|
||||
'Falha ao carregar Relatórios. Verifique se a API está rodando e o endpoint /api/relatorios/dashboard está acessível.';
|
||||
'Falha ao carregar Dashboard. Verifique se a API está rodando e o endpoint /api/dashboard está acessível.';
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchDashboardReal(): Promise<RelatoriosDashboardDto> {
|
||||
private async fetchDashboardReal(): Promise<DashboardDto> {
|
||||
if (!isPlatformBrowser(this.platformId)) throw new Error('SSR não suportado para charts');
|
||||
const url = `${this.baseApi}/relatorios/dashboard`;
|
||||
return await firstValueFrom(this.http.get<RelatoriosDashboardDto>(url));
|
||||
const url = `${this.baseApi}/dashboard`;
|
||||
return await firstValueFrom(this.http.get<DashboardDto>(url));
|
||||
}
|
||||
|
||||
private applyDto(dto: RelatoriosDashboardDto) {
|
||||
private applyDto(dto: DashboardDto) {
|
||||
const k = dto.kpis;
|
||||
|
||||
this.kpis = [
|
||||
|
|
@ -117,18 +117,18 @@ export class LoginComponent {
|
|||
const nome = this.getNameFromToken(token);
|
||||
console.log('👤 Nome extraído:', nome);
|
||||
|
||||
console.log('🔄 Tentando ir para /relatorios...');
|
||||
this.router.navigate(['/relatorios'], {
|
||||
console.log('🔄 Tentando ir para /dashboard...');
|
||||
this.router.navigate(['/dashboard'], {
|
||||
state: { toastMessage: `Bem-vindo, ${nome}!` }
|
||||
}).then(sucesso => {
|
||||
if (sucesso) console.log('✅ Navegação funcionou!');
|
||||
else console.error('❌ Navegação falhou! A rota "/relatorios" existe?');
|
||||
else console.error('❌ Navegação falhou! A rota "/dashboard" existe?');
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('❌ Erro ao processar token ou navegar:', e);
|
||||
// Força a ida mesmo se o nome falhar
|
||||
this.router.navigate(['/relatorios']);
|
||||
this.router.navigate(['/dashboard']);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue