266 lines
9.9 KiB
HTML
266 lines
9.9 KiB
HTML
<section class="parcelamentos-page">
|
|
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 12000;">
|
|
<div class="toast border-0 shadow" [class.show]="toastOpen" [class.text-bg-success]="toastType === 'success'" [class.text-bg-danger]="toastType === 'danger'">
|
|
<div class="toast-header border-bottom-0">
|
|
<strong class="me-auto">LineGestao</strong>
|
|
<button type="button" class="btn-close" (click)="toastOpen = false"></button>
|
|
</div>
|
|
<div class="toast-body bg-white rounded-bottom text-dark fw-bold">{{ toastMessage }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container-geral-responsive">
|
|
<div class="parcelamentos-shell">
|
|
<header class="page-header">
|
|
<div class="page-header-main">
|
|
<div class="title-group">
|
|
<span class="title-badge"><i class="bi bi-wallet2"></i> PARCELAMENTOS</span>
|
|
<div class="header-title">
|
|
<h2>Gestao de Parcelamentos</h2>
|
|
<p>Painel administrativo de parcelas de aparelhos e contratos</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="header-actions">
|
|
<button class="btn-ghost" type="button" (click)="refresh()" [disabled]="loading">
|
|
<i class="bi bi-arrow-repeat"></i> Atualizar
|
|
</button>
|
|
<button class="btn-ghost" type="button" (click)="onExport()" [disabled]="loading || exporting">
|
|
<span *ngIf="!exporting"><i class="bi bi-download"></i> Exportar</span>
|
|
<span *ngIf="exporting"><span class="spinner-border spinner-border-sm me-2"></span> Exportando...</span>
|
|
</button>
|
|
<button class="btn-primary" type="button" (click)="openCreateModal()">
|
|
<i class="bi bi-plus-circle"></i> Novo Parcelamento
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="header-highlights" aria-label="Resumo da listagem">
|
|
<div class="highlight-card">
|
|
<span>Total de registros</span>
|
|
<strong>{{ total }}</strong>
|
|
</div>
|
|
<div class="highlight-card">
|
|
<span>Pagina atual</span>
|
|
<strong>{{ page }} de {{ totalPages }}</strong>
|
|
</div>
|
|
<div class="highlight-card">
|
|
<span>Segmento ativo</span>
|
|
<strong>
|
|
{{ activeSegment === 'todos' ? 'Lista geral' : (activeSegment === 'ativos' ? 'Ativos' : (activeSegment === 'futuros' ? 'Futuros' : 'Finalizados')) }}
|
|
</strong>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<app-parcelamentos-kpis [cards]="kpiCards"></app-parcelamentos-kpis>
|
|
|
|
<app-parcelamentos-filters
|
|
[filters]="filters"
|
|
[monthOptions]="monthOptions"
|
|
[loading]="loading"
|
|
[competenciaInvalid]="competenciaInvalid"
|
|
[activeChips]="activeChips"
|
|
(apply)="applyFilters()"
|
|
(clear)="clearFilters()"
|
|
(searchChange)="onSearchChange($event)">
|
|
</app-parcelamentos-filters>
|
|
|
|
<app-parcelamentos-table
|
|
[items]="viewItems"
|
|
[loading]="loading"
|
|
[errorMessage]="errorMessage"
|
|
[segment]="activeSegment"
|
|
[segmentCounts]="segmentCounts"
|
|
[page]="page"
|
|
[pageNumbers]="pageNumbers"
|
|
[pageStart]="pageStart"
|
|
[pageEnd]="pageEnd"
|
|
[total]="total"
|
|
[pageSize]="pageSize"
|
|
[pageSizeOptions]="pageSizeOptions"
|
|
[isSysAdmin]="isSysAdmin"
|
|
(segmentChange)="setSegment($event)"
|
|
(detail)="openDetails($event)"
|
|
(edit)="openEdit($event)"
|
|
(remove)="openDelete($event)"
|
|
(pageChange)="goToPage($event)"
|
|
(pageSizeChange)="onPageSizeChange($event)">
|
|
</app-parcelamentos-table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Modal detalhes -->
|
|
<div class="lg-backdrop" *ngIf="detailOpen" (click)="closeDetails()"></div>
|
|
<div class="lg-modal" *ngIf="detailOpen">
|
|
<div class="lg-modal-card parcelamento-modal" (click)="$event.stopPropagation()">
|
|
<div class="modal-header">
|
|
<div class="modal-title">
|
|
<span class="icon-bg"><i class="bi bi-card-list"></i></span>
|
|
<span>Detalhes do Parcelamento</span>
|
|
</div>
|
|
<div class="modal-actions">
|
|
<button class="btn-icon" type="button" (click)="closeDetails()" aria-label="Fechar modal">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<div class="detail-state" *ngIf="detailLoading && !selectedDetail">
|
|
<div class="spinner-border text-brand" role="status"></div>
|
|
<span>Carregando detalhes...</span>
|
|
</div>
|
|
|
|
<div class="detail-state error" *ngIf="!detailLoading && detailError && !selectedDetail">
|
|
<i class="bi bi-exclamation-triangle"></i>
|
|
<span>{{ detailError }}</span>
|
|
</div>
|
|
|
|
<ng-container *ngIf="selectedDetail as detail">
|
|
<div class="detail-grid">
|
|
<div class="detail-card">
|
|
<small>Cliente</small>
|
|
<span class="detail-strong">{{ detail.cliente || '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Linha</small>
|
|
<span class="detail-strong text-blue">{{ detail.linha || '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>AnoRef</small>
|
|
<span>{{ detail.anoRef ?? '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Item</small>
|
|
<span>{{ detail.item ?? '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Qt Parcelas</small>
|
|
<span>{{ displayQtParcelas(detail) }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Parcela Atual</small>
|
|
<span class="detail-strong">{{ detail.parcelaAtual ?? '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Total Parcelas</small>
|
|
<span>{{ detail.totalParcelas ?? '-' }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Status</small>
|
|
<span class="status-pill">{{ detailStatus }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Valor Cheio</small>
|
|
<span>{{ formatMoney(detail.valorCheio) }}</span>
|
|
</div>
|
|
<div class="detail-card">
|
|
<small>Desconto</small>
|
|
<span class="text-danger">{{ formatMoney(detail.desconto) }}</span>
|
|
</div>
|
|
<div class="detail-card highlight">
|
|
<small>Valor com Desconto</small>
|
|
<span class="detail-strong money-strong">{{ formatMoney(detail.valorComDesconto) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="annual-section">
|
|
<div class="annual-head">
|
|
<div class="section-title">
|
|
<i class="bi bi-table"></i>
|
|
<span>Detalhamento anual</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="annual-table-shell" *ngIf="annualRows.length > 0; else annualEmpty">
|
|
<table class="table-modern annual-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="sticky-col col-1">Ano</th>
|
|
<th class="sticky-col col-2 text-end">Total</th>
|
|
<th *ngFor="let m of annualMonthHeaders" class="text-end">{{ m.label }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let row of annualRows">
|
|
<td class="sticky-col col-1">{{ row.year }}</td>
|
|
<td class="sticky-col col-2 text-end">{{ row.total | currency:'BRL':'symbol':'1.2-2':'pt-BR' }}</td>
|
|
<td *ngFor="let m of row.months" class="text-end">
|
|
{{ m.value !== null && m.value !== undefined ? (m.value | currency:'BRL':'symbol':'1.2-2':'pt-BR') : '-' }}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<ng-template #annualEmpty>
|
|
<div class="annual-empty">
|
|
Sem dados anuais.
|
|
</div>
|
|
</ng-template>
|
|
</div>
|
|
</ng-container>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button class="btn-primary" type="button" (click)="closeDetails()">Fechar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<app-parcelamento-create-modal
|
|
[open]="createOpen"
|
|
[model]="createModel"
|
|
[monthOptions]="monthOptions"
|
|
[loading]="createSaving"
|
|
[errorMessage]="createError"
|
|
title="Novo Parcelamento"
|
|
submitLabel="Salvar"
|
|
(close)="closeCreateModal()"
|
|
(save)="saveNewParcelamento($event)">
|
|
</app-parcelamento-create-modal>
|
|
|
|
<app-parcelamento-create-modal
|
|
*ngIf="editOpen && editModel"
|
|
[open]="editOpen"
|
|
[model]="editModel"
|
|
[monthOptions]="monthOptions"
|
|
[loading]="editSaving"
|
|
[errorMessage]="editError"
|
|
title="Editar Parcelamento"
|
|
submitLabel="Atualizar"
|
|
(close)="closeEditModal()"
|
|
(save)="saveEditParcelamento($event)">
|
|
</app-parcelamento-create-modal>
|
|
|
|
<!-- Delete modal -->
|
|
<div class="lg-backdrop" *ngIf="deleteOpen"></div>
|
|
<div class="lg-modal" *ngIf="deleteOpen">
|
|
<div class="lg-modal-card modal-compact" (click)="$event.stopPropagation()">
|
|
<div class="modal-header">
|
|
<div class="modal-title">
|
|
<span class="icon-bg danger-soft"><i class="bi bi-trash"></i></span>
|
|
Remover Parcelamento
|
|
</div>
|
|
<button class="btn-icon" type="button" (click)="cancelDelete()" aria-label="Fechar modal de exclusao">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="confirm-delete">
|
|
<div class="confirm-icon"><i class="bi bi-trash"></i></div>
|
|
<p class="mb-0">Confirma remover o parcelamento <strong>{{ deleteTarget?.linha }}</strong>?</p>
|
|
<small class="text-danger" *ngIf="deleteError">{{ deleteError }}</small>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn-ghost" type="button" (click)="cancelDelete()">Cancelar</button>
|
|
<button class="btn-danger" type="button" [disabled]="deleteLoading" (click)="confirmDelete()">
|
|
{{ deleteLoading ? 'Excluindo...' : 'Excluir' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|