616 lines
27 KiB
HTML
616 lines
27 KiB
HTML
<section class="resumo-page">
|
|
<div class="wrap">
|
|
<div class="resumo-container">
|
|
|
|
<div class="page-head" data-animate>
|
|
<div class="title-group">
|
|
<span class="badge-pill"><i class="bi bi-graph-up"></i> Dashboard</span>
|
|
<div class="flex-title">
|
|
<h2 class="page-title">Resumo Gerencial</h2>
|
|
<div class="status-wrapper">
|
|
<div class="status loading" *ngIf="loading">
|
|
<i class="bi bi-arrow-repeat spin"></i> Atualizando dados...
|
|
</div>
|
|
<div class="status error" *ngIf="!loading && errorMessage">
|
|
<i class="bi bi-exclamation-triangle"></i> {{ errorMessage }}
|
|
</div>
|
|
<div class="status success" *ngIf="!loading && !errorMessage">
|
|
<i class="bi bi-check-circle"></i> Atualizado
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p class="page-subtitle">Visão consolidada de performance, contratos e indicadores operacionais.</p>
|
|
</div>
|
|
|
|
<div class="header-actions">
|
|
<button class="btn-ghost" type="button" (click)="refresh()" [disabled]="loading">
|
|
<i class="bi bi-arrow-clockwise" [class.spin]="loading"></i>
|
|
<span>Atualizar</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-bar" data-animate>
|
|
<button
|
|
type="button"
|
|
class="tab-btn"
|
|
*ngFor="let tab of tabs"
|
|
[class.active]="activeTab === tab.key"
|
|
(click)="setTab(tab.key)">
|
|
<i [class]="tab.icon"></i>
|
|
<span>{{ tab.label }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="tab-panel" *ngIf="activeTab === 'planos'">
|
|
<div class="section-hero" data-animate>
|
|
<div class="hero-content">
|
|
<div class="hero-text">
|
|
<h3>Planos & Contratos</h3>
|
|
<p>{{ showFinancial ? 'Performance financeira agrupada por modalidade de plano.' : 'Distribuição e volume de linhas por modalidade de plano.' }}</p>
|
|
</div>
|
|
<div class="hero-kpis">
|
|
<div class="kpi-card kpi-card--total-lines">
|
|
<span class="kpi-lbl">Total Linhas</span>
|
|
<strong class="kpi-val">{{ formatNumber(planosTotals?.totalLinhasTotal) }}</strong>
|
|
</div>
|
|
<div class="kpi-card" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Valor Total</span>
|
|
<strong class="kpi-val text-brand">{{ formatMoney(planosTotals?.valorTotal) }}</strong>
|
|
</div>
|
|
<div class="kpi-card" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Contratos</span>
|
|
<strong class="kpi-val">{{ formatMoney(contratosTotals?.valorTotal) }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-grid planos-charts" data-animate>
|
|
<div class="chart-card" *ngIf="showFinancial">
|
|
<div class="card-header-clean">
|
|
<h3>Top Planos (Valor)</h3>
|
|
<p>Os planos com maior representatividade financeira.</p>
|
|
</div>
|
|
<div class="chart-area">
|
|
<canvas #chartPlanos></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="chart-card" [class.full-span]="!showFinancial">
|
|
<div class="card-header-clean">
|
|
<h3>Top Planos (Volume)</h3>
|
|
<p>Quantidade de linhas ativas por tipo de plano.</p>
|
|
</div>
|
|
<div class="chart-area">
|
|
<canvas #chartPlanosLinhas></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details class="section-card" open>
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Macrophony - Planos</h4>
|
|
<span>Detalhamento granular dos planos e suas variações.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
<div class="macrophony-block" [class.compact]="macrophonyCompact">
|
|
<div class="table-tools">
|
|
<div class="search-box">
|
|
<i class="bi bi-search"></i>
|
|
<input
|
|
type="text"
|
|
placeholder="Pesquisar..."
|
|
[value]="macrophonySearch"
|
|
(input)="onMacrophonySearch($any($event.target).value)" />
|
|
</div>
|
|
|
|
<div class="tools-right">
|
|
<label class="select-label">
|
|
Exibir
|
|
<select [value]="macrophonyPageSize" (change)="onMacrophonyPageSizeChange($any($event.target).value)">
|
|
<option *ngFor="let size of macrophonyPageOptions" [value]="size">{{ size }}</option>
|
|
</select>
|
|
</label>
|
|
<div class="divider-v"></div>
|
|
<button class="btn-icon-text" type="button" (click)="toggleMacrophonyCompact()">
|
|
<i class="bi" [class.bi-arrows-angle-expand]="macrophonyCompact" [class.bi-arrows-collapse]="!macrophonyCompact"></i>
|
|
<span class="hide-mobile">{{ macrophonyCompact ? 'Expandir' : 'Compactar' }}</span>
|
|
</button>
|
|
<button class="btn-icon-text" type="button" (click)="exportMacrophonyCsv()">
|
|
<i class="bi bi-download"></i>
|
|
<span class="hide-mobile">Exportar</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="macrophony-list">
|
|
<div class="empty-state" *ngIf="!loading && macrophonyView.length === 0">
|
|
<i class="bi bi-inbox"></i>
|
|
<p>Nenhum dado encontrado.</p>
|
|
</div>
|
|
|
|
<div class="macrophony-group" *ngFor="let group of macrophonyView; trackBy: trackByIndex">
|
|
<div class="macrophony-row" (click)="toggleMacrophonyGroup(group.key)">
|
|
<div class="row-trigger">
|
|
<button class="group-toggle" type="button">
|
|
<i class="bi" [class.bi-chevron-down]="isMacrophonyOpen(group.key)" [class.bi-chevron-right]="!isMacrophonyOpen(group.key)"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="group-main">
|
|
<div class="group-title">{{ group.plano }}</div>
|
|
<div class="group-meta">
|
|
<span class="badge-tag">GB {{ group.gbLabel }}</span>
|
|
<span class="badge-tag secondary">{{ formatNumber(group.totalLinhas) }} linhas</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="group-metrics" *ngIf="showFinancial">
|
|
<div class="metric">
|
|
<span class="lbl">Valor Total</span>
|
|
<strong class="val-money">{{ formatMoney(group.valorTotal) }}</strong>
|
|
</div>
|
|
<div class="metric hide-mobile">
|
|
<span class="lbl">Média Un.</span>
|
|
<strong>{{ formatMoney(group.valorUnitMedio) }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="group-actions">
|
|
<button class="btn-mini" type="button" (click)="openMacrophonyDetail(group); $event.stopPropagation()">
|
|
Detalhes
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="macrophony-details" *ngIf="isMacrophonyOpen(group.key)">
|
|
<div class="table-wrap is-nested">
|
|
<table class="data-table" [class.compact]="macrophonyCompact">
|
|
<thead>
|
|
<tr>
|
|
<th>Plano / Variação</th>
|
|
<th>Franquia</th>
|
|
<th class="text-right" *ngIf="showFinancial">Valor Un.</th>
|
|
<th class="text-right">Linhas</th>
|
|
<th class="text-right" *ngIf="showFinancial">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of group.rows; trackBy: trackByIndex">
|
|
<td>
|
|
<div class="cell-flex">
|
|
{{ row.planoContrato || '-' }}
|
|
<i *ngIf="isVivoTravel(row.vivoTravel)" class="bi bi-airplane-fill text-brand" title="Vivo Travel"></i>
|
|
</div>
|
|
</td>
|
|
<td>{{ formatGb(row.gb) }}</td>
|
|
<td class="text-right num-font" *ngIf="showFinancial">{{ formatMoney(row.valorIndividualComSvas) }}</td>
|
|
<td class="text-right num-font">{{ formatNumber(row.totalLinhas) }}</td>
|
|
<td class="text-right num-font font-bold" *ngIf="showFinancial">{{ formatMoney(row.valorTotal) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-footer">
|
|
<div class="table-count">
|
|
Exibindo {{ macrophonyPageStart }}-{{ macrophonyPageEnd }} de {{ macrophonyFilteredGroups.length }} grupos
|
|
</div>
|
|
<div class="pagination">
|
|
<button class="page-btn" (click)="goToMacrophonyPage(macrophonyPage - 1)" [disabled]="macrophonyPage === 1">
|
|
<i class="bi bi-chevron-left"></i>
|
|
</button>
|
|
<button
|
|
class="page-btn"
|
|
*ngFor="let p of macrophonyPageNumbers"
|
|
[class.active]="p === macrophonyPage"
|
|
(click)="goToMacrophonyPage(p)">{{ p }}</button>
|
|
<button class="page-btn" (click)="goToMacrophonyPage(macrophonyPage + 1)" [disabled]="macrophonyPage === macrophonyTotalPages">
|
|
<i class="bi bi-chevron-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="macrophony-summary" *ngIf="planosTotals">
|
|
<div class="summary-item">
|
|
<span>Total de Linhas</span>
|
|
<strong>{{ formatNumber(planosTotals.totalLinhasTotal) }}</strong>
|
|
</div>
|
|
<div class="summary-item highlight" *ngIf="showFinancial">
|
|
<span>Valor Total Global</span>
|
|
<strong>{{ formatMoney(planosTotals.valorTotal) }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="section-card">
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Resumo de Contratos</h4>
|
|
<span>Visão consolidada por tipo de contrato vigente.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
<ng-container *ngTemplateOutlet="groupedTableBlock; context: { group: groupPlanoContrato, footer: 'contratos', file: 'plano-contrato' }"></ng-container>
|
|
</details>
|
|
</div>
|
|
|
|
<div class="tab-panel" *ngIf="activeTab === 'clientes'">
|
|
<div class="section-hero" data-animate>
|
|
<div class="hero-content">
|
|
<div class="hero-text">
|
|
<h3>Clientes & Performance</h3>
|
|
<p>{{ showFinancial ? 'Analise a rentabilidade e custos por cliente.' : 'Distribuição e volume de linhas por cliente.' }}</p>
|
|
</div>
|
|
<div class="hero-kpis">
|
|
<div class="kpi-card kpi-card--total-lines">
|
|
<span class="kpi-lbl">Total Linhas</span>
|
|
<strong class="kpi-val">{{ formatNumber(clientesTotals?.qtdLinhasTotal) }}</strong>
|
|
</div>
|
|
<div class="kpi-card" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Receita Line</span>
|
|
<strong class="kpi-val">{{ formatMoney(clientesTotals?.valorContratoLine) }}</strong>
|
|
</div>
|
|
<div class="kpi-card highlight" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Lucro Total</span>
|
|
<strong class="kpi-val text-success">{{ formatMoney(clientesTotals?.lucro) }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-grid full-chart" data-animate>
|
|
<div class="chart-card">
|
|
<div class="card-header-clean">
|
|
<h3>{{ showFinancial ? 'Top Clientes (Lucratividade)' : 'Top Clientes (Qtd. Linhas)' }}</h3>
|
|
<p>{{ showFinancial ? 'Clientes ordenados pelo maior retorno financeiro.' : 'Clientes com maior volume de linhas.' }}</p>
|
|
</div>
|
|
<div class="chart-area">
|
|
<canvas #chartClientes></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details class="section-card" open>
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Detalhamento Vivo x Line Móvel</h4>
|
|
<span>Comparativo de custos, receitas e margem por cliente.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
<ng-container *ngTemplateOutlet="groupedTableBlock; context: { group: groupClientes, footer: 'clientes', file: 'clientes-vivo-line' }"></ng-container>
|
|
</details>
|
|
|
|
</div>
|
|
|
|
<div class="tab-panel" *ngIf="activeTab === 'totais'">
|
|
<div class="section-hero" data-animate>
|
|
<div class="hero-content">
|
|
<div class="hero-text">
|
|
<h3>Totais Line</h3>
|
|
<p>Consolidado entre Pessoa Física (PF) e Jurídica (PJ).</p>
|
|
</div>
|
|
<div class="hero-kpis">
|
|
<div class="kpi-card">
|
|
<span class="kpi-lbl">PF Linhas</span>
|
|
<strong class="kpi-val">{{ formatNumber(findLineTotal(['PF','PESSOA FISICA'])?.qtdLinhas) }}</strong>
|
|
</div>
|
|
<div class="kpi-card">
|
|
<span class="kpi-lbl">PJ Linhas</span>
|
|
<strong class="kpi-val">{{ formatNumber(findLineTotal(['PJ','PESSOA JURIDICA'])?.qtdLinhas) }}</strong>
|
|
</div>
|
|
<div class="kpi-card" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Lucro Consolidado</span>
|
|
<strong class="kpi-val text-success">{{ formatMoney(totaisLineLucroConsolidado) }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-grid full-chart" data-animate>
|
|
<div class="chart-card">
|
|
<div class="card-header-clean">
|
|
<h3>Distribuição PF vs PJ</h3>
|
|
<p>Proporção da base de linhas ativas.</p>
|
|
</div>
|
|
<div class="chart-area">
|
|
<canvas #chartTotais></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details class="section-card" open>
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Detalhamento Totais</h4>
|
|
<span>Tabela analítica dos totais processados.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
<ng-container *ngTemplateOutlet="groupedTableBlock; context: { group: groupTotaisLine, footer: 'none', file: 'totais-line' }"></ng-container>
|
|
</details>
|
|
|
|
<details class="section-card" open>
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Distribuição por GB</h4>
|
|
<span>Tabela GB / QTD / SOMA importada da aba RESUMO.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
|
|
<div class="grouped-block">
|
|
<div class="table-wrap">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>GB</th>
|
|
<th class="text-right">QTD</th>
|
|
<th class="text-right">SOMA</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of gbDistribuicaoRows; trackBy: trackByIndex">
|
|
<td><span class="num-font">{{ formatGb(row.gb) }}</span></td>
|
|
<td class="text-right"><span class="num-font">{{ formatNumber(row.qtd) }}</span></td>
|
|
<td class="text-right"><span class="num-font">{{ formatMoney(row.soma) }}</span></td>
|
|
</tr>
|
|
<tr class="total-row" *ngIf="gbDistribuicaoRows.length">
|
|
<td>Total</td>
|
|
<td class="text-right"><span class="num-font">{{ formatNumber(gbDistribuicaoTotalLinhas) }}</span></td>
|
|
<td class="text-right"><span class="num-font">{{ formatMoney(gbDistribuicaoSomaTotal) }}</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<div class="tab-panel" *ngIf="activeTab === 'reserva'">
|
|
<div class="section-hero" data-animate>
|
|
<div class="hero-content">
|
|
<div class="hero-text">
|
|
<h3>Estoque de Reserva</h3>
|
|
<p>Monitoramento de linhas disponíveis por DDD.</p>
|
|
</div>
|
|
<div class="hero-kpis">
|
|
<div class="kpi-card">
|
|
<span class="kpi-lbl">Linhas em Estoque</span>
|
|
<strong class="kpi-val">{{ formatNumber(reservaTotals?.qtdLinhasTotal) }}</strong>
|
|
</div>
|
|
<div class="kpi-card" *ngIf="showFinancial">
|
|
<span class="kpi-lbl">Custo de Reserva</span>
|
|
<strong class="kpi-val">{{ formatNumber(reservaTotals?.total) }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-grid full-chart" data-animate>
|
|
<div class="chart-card">
|
|
<div class="card-header-clean">
|
|
<h3>Concentração por DDD</h3>
|
|
<p>Regiões com maior volume de linhas em reserva.</p>
|
|
</div>
|
|
<div class="chart-area">
|
|
<canvas #chartReserva></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details class="section-card" open>
|
|
<summary>
|
|
<div class="summary-content">
|
|
<h4>Detalhamento por DDD</h4>
|
|
<span>Lista completa de estoque agrupada geograficamente.</span>
|
|
</div>
|
|
<div class="summary-icon"><i class="bi bi-chevron-down"></i></div>
|
|
</summary>
|
|
<ng-container *ngTemplateOutlet="groupedTableBlock; context: { group: groupReserva, footer: 'reserva', file: 'reserva-ddd' }"></ng-container>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<ng-template #groupedTableBlock let-group="group" let-footer="footer" let-file="file">
|
|
<div class="grouped-block" [class.compact]="group.compact">
|
|
<div class="table-tools">
|
|
<div class="search-box">
|
|
<i class="bi bi-search"></i>
|
|
<input
|
|
type="text"
|
|
placeholder="Pesquisar..."
|
|
[value]="group.search"
|
|
(input)="onGroupedSearch(group, $any($event.target).value)" />
|
|
</div>
|
|
|
|
<div class="tools-right">
|
|
<button class="btn-icon-text" type="button" (click)="toggleGroupedCompact(group)">
|
|
<i class="bi" [class.bi-arrows-angle-expand]="group.compact" [class.bi-arrows-collapse]="!group.compact"></i>
|
|
<span class="hide-mobile">{{ group.compact ? 'Expandir' : 'Compactar' }}</span>
|
|
</button>
|
|
<button class="btn-icon-text" type="button" (click)="exportGroupedCsv(group, file)">
|
|
<i class="bi bi-download"></i>
|
|
<span class="hide-mobile">Exportar</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grouped-list">
|
|
<div class="empty-state" *ngIf="!loading && group.view.length === 0">
|
|
<p>Nenhum registro encontrado.</p>
|
|
</div>
|
|
<div class="grouped-group" *ngFor="let item of group.view; trackBy: trackByIndex">
|
|
<div class="grouped-row" (click)="toggleGroupedOpen(group, item.key)">
|
|
<div class="row-trigger">
|
|
<button class="group-toggle" type="button">
|
|
<i class="bi" [class.bi-chevron-down]="isGroupedOpen(group, item.key)" [class.bi-chevron-right]="!isGroupedOpen(group, item.key)"></i>
|
|
</button>
|
|
</div>
|
|
<div class="group-main">
|
|
<div class="group-title">{{ item.title }}</div>
|
|
<div class="group-subtitle" *ngIf="item.subtitle">{{ item.subtitle }}</div>
|
|
</div>
|
|
<div class="group-metrics">
|
|
<div class="metric" *ngFor="let metric of item.metrics">
|
|
<span class="lbl">{{ metric.label }}</span>
|
|
<strong class="num-font" [ngClass]="metric.tone">{{ metric.value }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="group-actions">
|
|
<button class="btn-mini" type="button" (click)="openGroupedDetail(group, item); $event.stopPropagation()">
|
|
Ver Tabela
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grouped-details" *ngIf="isGroupedOpen(group, item.key)">
|
|
<div class="table-wrap is-nested">
|
|
<table class="data-table" [class.compact]="group.compact">
|
|
<thead>
|
|
<tr>
|
|
<th
|
|
*ngFor="let col of group.table.columns"
|
|
[class.text-right]="col.align === 'right'"
|
|
[class.text-center]="col.align === 'center'">
|
|
{{ col.label }}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of item.rows; trackBy: trackByIndex" [class.diff-row]="getTableRowClass(group.table, row)">
|
|
<td
|
|
*ngFor="let col of group.table.columns"
|
|
[class.text-right]="col.align === 'right'"
|
|
[class.text-center]="col.align === 'center'">
|
|
<span class="num-font" *ngIf="!col.badge" [ngClass]="col.tone ? getToneClass(col.value(row)) : null">
|
|
{{ formatCell(col, row) }}
|
|
</span>
|
|
<span *ngIf="col.badge" class="badge-tag">{{ formatCell(col, row) }}</span>
|
|
<i *ngIf="col.icon && col.icon(row)" [class]="col.icon(row)"></i>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grouped-summary-bar" *ngIf="showFinancial && footer === 'clientes' && clientesTotals">
|
|
<div class="sum-col">
|
|
<span class="lbl">Receita Line</span>
|
|
<strong>{{ formatMoney(clientesTotals.valorContratoLine) }}</strong>
|
|
</div>
|
|
<div class="sum-col">
|
|
<span class="lbl">Custo Vivo</span>
|
|
<strong>{{ formatMoney(clientesTotals.valorContratoVivo) }}</strong>
|
|
</div>
|
|
<div class="sum-col highlight">
|
|
<span class="lbl">Lucro Líquido</span>
|
|
<strong class="text-success">{{ formatMoney(clientesTotals.lucro) }}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-footer">
|
|
<div class="table-count">Mostrando {{ getGroupedPageStart(group) }}-{{ getGroupedPageEnd(group) }} de {{ group.filtered.length }}</div>
|
|
<div class="pagination">
|
|
<button class="page-btn" (click)="goToGroupedPage(group, group.page - 1)" [disabled]="group.page === 1">
|
|
<i class="bi bi-chevron-left"></i>
|
|
</button>
|
|
<button
|
|
class="page-btn"
|
|
*ngFor="let p of getGroupedPageNumbers(group)"
|
|
[class.active]="p === group.page"
|
|
(click)="goToGroupedPage(group, p)">{{ p }}</button>
|
|
<button class="page-btn" (click)="goToGroupedPage(group, group.page + 1)" [disabled]="group.page === getGroupedTotalPages(group)">
|
|
<i class="bi bi-chevron-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grouped-backdrop" *ngIf="group.detailOpen" (click)="closeGroupedDetail(group)"></div>
|
|
<div class="grouped-modal" *ngIf="group.detailOpen">
|
|
<div class="grouped-card" (click)="$event.stopPropagation()">
|
|
<div class="detail-head">
|
|
<h4>{{ group.detailGroup?.title }}</h4>
|
|
<button class="btn-icon" type="button" (click)="closeGroupedDetail(group)"><i class="bi bi-x-lg"></i></button>
|
|
</div>
|
|
<div class="grouped-modal-body">
|
|
<div class="table-wrap">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th *ngFor="let col of group.table.columns" [class.text-right]="col.align === 'right'">
|
|
{{ col.label }}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of group.detailGroup?.rows; trackBy: trackByIndex">
|
|
<td *ngFor="let col of group.table.columns" [class.text-right]="col.align === 'right'">
|
|
<span class="num-font">{{ formatCell(col, row) }}</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ng-template>
|
|
|
|
<div class="macrophony-backdrop" *ngIf="macrophonyDetailOpen" (click)="closeMacrophonyDetail()"></div>
|
|
<div class="macrophony-modal" *ngIf="macrophonyDetailOpen">
|
|
<div class="macrophony-card" (click)="$event.stopPropagation()">
|
|
<div class="detail-head">
|
|
<div>
|
|
<span class="detail-super">Detalhes do Plano</span>
|
|
<h4>{{ macrophonyDetailGroup?.plano }}</h4>
|
|
</div>
|
|
<button class="btn-icon" type="button" (click)="closeMacrophonyDetail()"><i class="bi bi-x-lg"></i></button>
|
|
</div>
|
|
<div class="macrophony-modal-body">
|
|
<div class="table-wrap">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Variação</th>
|
|
<th>GB</th>
|
|
<th class="text-right" *ngIf="showFinancial">Valor Un.</th>
|
|
<th class="text-right">Total Linhas</th>
|
|
<th class="text-right" *ngIf="showFinancial">Valor Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of macrophonyDetailGroup?.rows; trackBy: trackByIndex">
|
|
<td>{{ row.planoContrato || '-' }}</td>
|
|
<td>{{ formatGb(row.gb) }}</td>
|
|
<td class="text-right num-font" *ngIf="showFinancial">{{ formatMoney(row.valorIndividualComSvas) }}</td>
|
|
<td class="text-right num-font">{{ formatNumber(row.totalLinhas) }}</td>
|
|
<td class="text-right num-font" *ngIf="showFinancial">{{ formatMoney(row.valorTotal) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot *ngIf="macrophonyDetailGroup">
|
|
<tr class="total-row">
|
|
<td [attr.colspan]="showFinancial ? 3 : 2">Total deste grupo</td>
|
|
<td class="text-right num-font">{{ formatNumber(macrophonyDetailGroup.totalLinhas) }}</td>
|
|
<td class="text-right num-font" *ngIf="showFinancial">{{ formatMoney(macrophonyDetailGroup.valorTotal) }}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|