210 lines
8.5 KiB
HTML
210 lines
8.5 KiB
HTML
<section class="notificacoes-page">
|
|
<div class="wrap">
|
|
<div class="main-container">
|
|
|
|
<div class="page-header">
|
|
<div class="header-text">
|
|
<h2>Central de Notificações</h2>
|
|
<p>Gerencie seus alertas de vencimento e avisos do sistema.</p>
|
|
</div>
|
|
|
|
<div class="filters-bar">
|
|
<button type="button" class="pill" [class.active]="filter === 'todas'" (click)="setFilter('todas')">
|
|
Todas
|
|
</button>
|
|
<button type="button" class="pill" [class.active]="filter === 'aVencer'" (click)="setFilter('aVencer')">
|
|
A vencer
|
|
<span class="count-badge" *ngIf="countByType('AVencer') > 0">{{ countByType('AVencer') }}</span>
|
|
</button>
|
|
<button type="button" class="pill" [class.active]="filter === 'vencidas'" (click)="setFilter('vencidas')">
|
|
Vencidas
|
|
<span class="count-badge danger" *ngIf="countByType('Vencido') > 0">{{ countByType('Vencido') }}</span>
|
|
</button>
|
|
<button type="button" class="pill" [class.active]="filter === 'lidas'" (click)="setFilter('lidas')">
|
|
Arquivadas / Lidas
|
|
</button>
|
|
</div>
|
|
|
|
<div class="search-row" *ngIf="!loading && !error">
|
|
<div class="search-box">
|
|
<i class="bi bi-search"></i>
|
|
<input
|
|
type="text"
|
|
placeholder="Pesquisar..."
|
|
[(ngModel)]="search"
|
|
(ngModelChange)="clearSelection()"
|
|
/>
|
|
<button
|
|
type="button"
|
|
class="clear-btn"
|
|
*ngIf="search"
|
|
(click)="clearSearch()"
|
|
aria-label="Limpar busca"
|
|
>
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bulk-actions-bar" *ngIf="!loading && !error">
|
|
<div class="bulk-left">
|
|
<label class="select-all" *ngIf="filteredNotifications.length > 0">
|
|
<input type="checkbox" [checked]="isAllSelected" (change)="toggleSelectAll()" />
|
|
<span>Selecionar todas</span>
|
|
</label>
|
|
<span class="bulk-count">
|
|
Mostrando {{ filteredNotifications.length }} notificações
|
|
<span class="bulk-selected" *ngIf="selectedIds.size > 0">• {{ selectedIds.size }} selecionada(s)</span>
|
|
</span>
|
|
</div>
|
|
<div class="bulk-actions" *ngIf="filter !== 'lidas'">
|
|
<button
|
|
type="button"
|
|
class="bulk-btn"
|
|
(click)="markAllAsRead()"
|
|
[disabled]="bulkLoading || filteredNotifications.length === 0"
|
|
>
|
|
<span *ngIf="!bulkLoading"><i class="bi bi-check2-all me-1"></i> Ler todas</span>
|
|
<span *ngIf="bulkLoading"><span class="spinner-border spinner-border-sm me-2"></span> Marcando...</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="bulk-btn ghost"
|
|
(click)="exportNotifications()"
|
|
[disabled]="exportLoading || filteredNotifications.length === 0"
|
|
>
|
|
<span *ngIf="!exportLoading"><i class="bi bi-download me-1"></i> Exportar</span>
|
|
<span *ngIf="exportLoading"><span class="spinner-border spinner-border-sm me-2"></span> Exportando...</span>
|
|
</button>
|
|
</div>
|
|
<div class="bulk-actions" *ngIf="filter === 'lidas'">
|
|
<button
|
|
type="button"
|
|
class="bulk-btn"
|
|
(click)="markAllAsUnread()"
|
|
[disabled]="bulkUnreadLoading || filteredNotifications.length === 0"
|
|
>
|
|
<span *ngIf="!bulkUnreadLoading"><i class="bi bi-arrow-counterclockwise me-1"></i> Restaurar selecionadas/todas</span>
|
|
<span *ngIf="bulkUnreadLoading"><span class="spinner-border spinner-border-sm me-2"></span> Restaurando...</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="state-container" *ngIf="loading">
|
|
<div class="spinner-border text-primary" role="status"></div>
|
|
<p>Atualizando...</p>
|
|
</div>
|
|
|
|
<div class="state-container error" *ngIf="!loading && error">
|
|
<i class="bi bi-wifi-off"></i>
|
|
<p>Não foi possível carregar as notificações.</p>
|
|
</div>
|
|
|
|
<div class="empty-state-large" *ngIf="!loading && !error && filteredNotifications.length === 0">
|
|
<div class="illustration">
|
|
<i class="bi bi-check-circle-fill"></i>
|
|
</div>
|
|
<h3>Tudo em dia!</h3>
|
|
<p *ngIf="filter === 'todas'">Não há notificações no momento.</p>
|
|
<p *ngIf="filter !== 'todas'">Nenhuma notificação neste filtro.</p>
|
|
</div>
|
|
|
|
<div class="notif-list" *ngIf="!loading && !error && filteredNotifications.length > 0">
|
|
<div
|
|
class="list-item"
|
|
*ngFor="let n of filteredNotifications"
|
|
[class.is-read]="n.lida"
|
|
[class.is-danger]="isVencido(n)"
|
|
[class.is-warning]="isAVencer(n)"
|
|
>
|
|
<div class="status-strip"></div>
|
|
|
|
<label class="item-select">
|
|
<input type="checkbox" [checked]="isSelected(n)" (change)="toggleSelection(n)" />
|
|
<span></span>
|
|
</label>
|
|
|
|
<div class="item-icon">
|
|
<i
|
|
class="bi"
|
|
[class.bi-x-circle-fill]="isVencido(n)"
|
|
[class.bi-clock-fill]="isAVencer(n)"
|
|
[class.bi-check2-circle]="isAutoRenew(n)">
|
|
</i>
|
|
</div>
|
|
|
|
<div class="item-content">
|
|
<div class="content-top">
|
|
<h4 class="item-title">
|
|
{{ n.linha || 'Linha Desconhecida' }}
|
|
<span class="separator">•</span>
|
|
<span class="item-client">{{ n.cliente || '-' }}</span>
|
|
</h4>
|
|
<div class="date-stack">
|
|
<span class="date-pill green">Efetivação: {{ formatDateLabel(n.dtEfetivacaoServico) }}</span>
|
|
<span class="date-pill red">Término: {{ formatDateLabel(n.dtTerminoFidelizacao) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="item-meta-grid">
|
|
<div class="meta-row">
|
|
<span class="meta-label">Conta</span>
|
|
<span class="meta-value">{{ n.conta || '-' }}</span>
|
|
</div>
|
|
<div class="meta-row">
|
|
<span class="meta-label">Usuário</span>
|
|
<span class="meta-value">{{ n.usuario || '-' }}</span>
|
|
</div>
|
|
<div class="meta-row">
|
|
<span class="meta-label">Plano</span>
|
|
<span class="meta-value">{{ n.planoContrato || '-' }}</span>
|
|
</div>
|
|
<div class="meta-row">
|
|
<span class="badge-tag" [class.danger]="isVencido(n)" [class.warn]="isAVencer(n)" [class.info]="isAutoRenew(n)">
|
|
{{ getStatusLabel(n) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="item-actions">
|
|
<button
|
|
type="button"
|
|
class="btn-action"
|
|
[title]="n.lida ? 'Marcar como não lida' : 'Marcar como lida'"
|
|
(click)="n.lida ? markAsUnread(n) : markAsRead(n)"
|
|
>
|
|
<i class="bi" [class.bi-arrow-counterclockwise]="n.lida" [class.bi-check2]="!n.lida"></i>
|
|
<span class="d-none d-md-inline">{{ n.lida ? 'Restaurar' : 'Marcar lida' }}</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn-action ghost"
|
|
*ngIf="n.vigenciaLineId || n.linha"
|
|
title="Abrir na página de vigência"
|
|
(click)="goToVigencia(n)"
|
|
>
|
|
<i class="bi bi-box-arrow-up-right"></i>
|
|
<span class="d-none d-md-inline">Abrir vigência</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn-action renew"
|
|
*ngIf="isAVencer(n)"
|
|
title="Programar renovação automática por mais 2 anos"
|
|
(click)="renewFromNotification(n)"
|
|
[disabled]="isRenewing(n)"
|
|
>
|
|
<i class="bi bi-arrow-repeat"></i>
|
|
<span class="d-none d-md-inline">{{ isRenewing(n) ? 'Aguarde...' : 'Renovar +2' }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|