diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 5d2acd6..a6ea6af 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -12,6 +12,7 @@ import { DadosUsuarios } from './pages/dados-usuarios/dados-usuarios'; import { VigenciaComponent } from './pages/vigencia/vigencia'; import { TrocaNumero } from './pages/troca-numero/troca-numero'; import { Dashboard } from './pages/dashboard/dashboard'; +import { Notificacoes } from './pages/notificacoes/notificacoes'; export const routes: Routes = [ { path: '', component: Home }, @@ -24,6 +25,7 @@ export const routes: Routes = [ { path: 'dadosusuarios', component: DadosUsuarios, canActivate: [authGuard] }, { path: 'vigencia', component: VigenciaComponent, canActivate: [authGuard] }, { path: 'trocanumero', component: TrocaNumero, canActivate: [authGuard] }, + { path: 'notificacoes', component: Notificacoes, canActivate: [authGuard] }, // ✅ rota correta { path: 'dashboard', component: Dashboard, canActivate: [authGuard] }, diff --git a/src/app/app.ts b/src/app/app.ts index 93920fa..021813a 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -34,6 +34,7 @@ export class AppComponent { '/vigencia', '/trocanumero', '/dashboard', // ✅ ADICIONADO: esconde footer na página de dashboard + '/notificacoes', ]; constructor( diff --git a/src/app/components/header/header.html b/src/app/components/header/header.html index ce0151e..de807c6 100644 --- a/src/app/components/header/header.html +++ b/src/app/components/header/header.html @@ -20,9 +20,49 @@
- +
+ + +
+
+ Notificações + Ver todas +
+ +
+
+ Carregando... +
+
+ Falha ao carregar notificações. +
+
+ Nenhuma notificação por aqui. +
+ +
+ + {{ n.tipo === 'Vencido' ? 'Vencido' : 'A vencer' }} + +
{{ n.titulo }}
+
{{ n.mensagem }}
+ +
+
+
+
+
+ +
+
+ + diff --git a/src/app/pages/notificacoes/notificacoes.scss b/src/app/pages/notificacoes/notificacoes.scss new file mode 100644 index 0000000..e2455b8 --- /dev/null +++ b/src/app/pages/notificacoes/notificacoes.scss @@ -0,0 +1,130 @@ +:host { + display: block; +} + +.notificacoes-page { + width: 100%; +} + +.wrap { + padding: 24px 0 32px; +} + +.container { + width: 100%; + max-width: 1100px; + margin: 0 auto; + padding: 0 16px; +} + +.page-head { + display: flex; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 20px; + + h2 { + font-size: 24px; + font-weight: 800; + margin: 0 0 4px; + } + + p { + margin: 0; + color: rgba(17, 18, 20, 0.6); + font-weight: 600; + } +} + +.state { + padding: 12px 14px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(0,0,0,0.08); + font-weight: 700; + color: rgba(17, 18, 20, 0.6); +} + +.state.warn { + color: #b45309; +} + +.notifications-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; +} + +.notification-card { + background: #fff; + border-radius: 16px; + border: 1px solid rgba(0,0,0,0.08); + padding: 16px; + box-shadow: 0 18px 36px rgba(0,0,0,0.08); + + h3 { + margin: 12px 0 8px; + font-size: 16px; + font-weight: 800; + color: rgba(17, 18, 20, 0.92); + } + + p { + margin: 0 0 12px; + font-size: 13px; + color: rgba(17, 18, 20, 0.68); + } +} + +.card-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.tag { + display: inline-flex; + align-items: center; + padding: 4px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 800; + background: rgba(3, 15, 170, 0.12); + color: #1f2937; +} + +.tag.warn { + background: rgba(227, 61, 207, 0.16); + color: #8b2a7d; +} + +.tag.danger { + background: rgba(239, 68, 68, 0.16); + color: #b91c1c; +} + +.date { + font-size: 12px; + color: rgba(17, 18, 20, 0.55); + font-weight: 700; +} + +.card-meta { + display: grid; + gap: 4px; + font-size: 12px; + color: rgba(17, 18, 20, 0.7); + font-weight: 700; +} + +.mark-read { + margin-top: 12px; + padding: 8px 12px; + border-radius: 10px; + border: 1px solid rgba(0,0,0,0.08); + background: #fff; + font-size: 12px; + font-weight: 700; + cursor: pointer; +} diff --git a/src/app/pages/notificacoes/notificacoes.ts b/src/app/pages/notificacoes/notificacoes.ts new file mode 100644 index 0000000..be6168b --- /dev/null +++ b/src/app/pages/notificacoes/notificacoes.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { NotificationsService, NotificationDto } from '../../services/notifications.service'; + +@Component({ + selector: 'app-notificacoes', + standalone: true, + imports: [CommonModule], + templateUrl: './notificacoes.html', + styleUrls: ['./notificacoes.scss'], +}) +export class Notificacoes implements OnInit { + notifications: NotificationDto[] = []; + loading = false; + error = false; + + constructor(private notificationsService: NotificationsService) {} + + ngOnInit(): void { + this.loadNotifications(); + } + + markAsRead(notification: NotificationDto) { + if (notification.lida) return; + this.notificationsService.markAsRead(notification.id).subscribe({ + next: () => { + notification.lida = true; + notification.lidaEm = new Date().toISOString(); + }, + }); + } + + private loadNotifications() { + this.loading = true; + this.error = false; + this.notificationsService.list().subscribe({ + next: (data) => { + this.notifications = data || []; + this.loading = false; + }, + error: () => { + this.error = true; + this.loading = false; + }, + }); + } +} diff --git a/src/app/services/notifications.service.ts b/src/app/services/notifications.service.ts new file mode 100644 index 0000000..a2ba36a --- /dev/null +++ b/src/app/services/notifications.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { environment } from '../../environments/environment'; + +export type NotificationTipo = 'AVencer' | 'Vencido'; + +export type NotificationDto = { + id: string; + tipo: NotificationTipo; + titulo: string; + mensagem: string; + data: string; + referenciaData?: string | null; + diasParaVencer?: number | null; + lida: boolean; + lidaEm?: string | null; + vigenciaLineId?: string | null; + cliente?: string | null; + linha?: string | null; +}; + +@Injectable({ providedIn: 'root' }) +export class NotificationsService { + private readonly baseUrl = `${environment.apiUrl}/notifications`; + + constructor(private http: HttpClient) {} + + list(): Observable { + return this.http.get(this.baseUrl); + } + + markAsRead(id: string): Observable { + return this.http.patch(`${this.baseUrl}/${id}/read`, {}); + } +}