Revert "Merge branch 'dev' into producao"

This reverts commit 5443acd33a, reversing
changes made to 6803ce9337.
This commit is contained in:
Eduardo Lopes 2026-03-12 16:29:07 -03:00
parent 2bd044a3ab
commit 469a2616a5
6 changed files with 79 additions and 214 deletions

View File

@ -7,7 +7,7 @@
[attr.aria-disabled]="disabled"
>
<span class="app-select-label">{{ displayLabel }}</span>
<i class="bi bi-chevron-down app-select-chevron"></i>
<i class="bi bi-chevron-down"></i>
</button>
<div class="app-select-panel" *ngIf="isOpen">

View File

@ -75,7 +75,8 @@
white-space: nowrap;
}
.app-select-trigger .app-select-chevron {
.app-select-trigger i {
position: absolute;
right: 8px;
top: 50%;

View File

@ -98,19 +98,27 @@
<button type="button" class="filter-tab" [class.active]="filterStatus === 'ACTIVE'" (click)="toggleActiveFilter()" [disabled]="loading">
<i class="bi bi-check2-circle me-1"></i> Ativos
</button>
<div class="filter-blocked-select-box">
<i class="bi bi-slash-circle blocked-status-select-icon" aria-hidden="true"></i>
<select
class="blocked-status-native-select"
[value]="blockedStatusMode"
(change)="onBlockedStatusSelectChange($event)"
[disabled]="loading"
>
<option *ngFor="let option of blockedStatusFilterOptions" [value]="option.value">
{{ option.label }}
</option>
</select>
</div>
<button type="button" class="filter-tab" [class.active]="filterStatus === 'BLOCKED'" (click)="toggleBlockedFilter()" [disabled]="loading">
<i class="bi bi-slash-circle me-1"></i> Bloqueadas
</button>
<ng-container *ngIf="filterStatus === 'BLOCKED'">
<button
type="button"
class="filter-tab"
[class.active]="blockedStatusMode === 'PERDA_ROUBO'"
(click)="setBlockedStatusMode('PERDA_ROUBO')"
[disabled]="loading">
Perda/Roubo
</button>
<button
type="button"
class="filter-tab"
[class.active]="blockedStatusMode === 'BLOQUEIO_120'"
(click)="setBlockedStatusMode('BLOQUEIO_120')"
[disabled]="loading">
120 dias
</button>
</ng-container>
</div>
</div>
@ -339,7 +347,16 @@
</button>
</div>
<div class="controls-right">
<div class="page-size d-flex align-items-center gap-2">
<span class="text-muted small fw-bold text-uppercase" style="letter-spacing: 0.5px; font-size: 0.75rem;">
Itens por pág:
</span>
<div class="select-wrapper">
<app-select class="select-glass" size="sm" [options]="pageSizeOptions" [(ngModel)]="pageSize" (ngModelChange)="onPageSizeChange()" [disabled]="loading"></app-select>
</div>
</div>
<div class="batch-status-tools" *ngIf="canManageLines">
<span class="batch-status-count">
Selecionadas: <strong>{{ batchStatusSelectionCount }}</strong>
@ -359,17 +376,6 @@
<i class="bi bi-unlock me-1"></i> Desbloquear em lote
</button>
</div>
<div class="page-size d-flex align-items-center gap-2">
<span class="text-muted small fw-bold text-uppercase" style="letter-spacing: 0.5px; font-size: 0.75rem;">
Itens por pág:
</span>
<div class="select-wrapper">
<app-select class="select-glass" size="sm" [options]="pageSizeOptions" [(ngModel)]="pageSize" (ngModelChange)="onPageSizeChange()" [disabled]="loading"></app-select>
</div>
</div>
</div>
</div>
</div>

View File

@ -143,88 +143,13 @@
.filters-row-top {
justify-content: center;
z-index: 60;
}
.filters-row-bottom {
justify-content: center;
z-index: 40;
}
.filter-tabs { display: flex; gap: 4px; padding: 4px; background: rgba(255, 255, 255, 0.6); border: 1px solid rgba(17, 18, 20, 0.08); border-radius: 12px; backdrop-filter: blur(8px); flex-wrap: wrap; justify-content: center; }
.filter-tab { border: none; background: transparent; padding: 8px 16px; border-radius: 8px; font-size: 0.85rem; font-weight: 700; color: var(--muted); transition: all 0.2s ease; display: flex; align-items: center; gap: 6px; flex: 0 0 auto; white-space: nowrap; line-height: 1.1; &:hover { color: var(--text); background: rgba(255, 255, 255, 0.5); } &.active { background: #fff; color: var(--brand); box-shadow: 0 2px 8px rgba(227, 61, 207, 0.15); } &:disabled { opacity: 0.5; cursor: not-allowed; } }
.filter-blocked-select-box { width: 196px; flex: 0 0 auto; position: relative; z-index: 80; }
.blocked-status-select-icon {
position: absolute;
left: 11px;
top: 50%;
transform: translateY(-50%);
z-index: 2;
pointer-events: none;
color: rgba(17, 18, 20, 0.68);
font-size: 0.82rem;
}
.blocked-status-native-select {
width: 100%;
height: 36px;
border-radius: 12px;
border: 1px solid rgba(17, 18, 20, 0.15);
background: rgba(255, 255, 255, 0.7);
color: #0f172a;
font-size: 0.78rem;
font-weight: 800;
padding: 0 30px 0 30px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
appearance: none;
-webkit-appearance: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
&:hover {
background: #fff;
border-color: rgba(17, 18, 20, 0.7);
box-shadow: 0 4px 12px rgba(3, 15, 170, 0.1);
}
&:focus {
outline: none;
border-color: #e33dcf;
box-shadow: 0 0 0 3px rgba(227, 61, 207, 0.15);
}
&:disabled {
background-color: #f1f5f9;
color: #94a3b8;
cursor: not-allowed;
}
}
@media (max-width: 1366px) {
.filter-tabs {
gap: 3px;
padding: 3px;
}
.filter-tab {
padding: 7px 12px;
font-size: 0.78rem;
gap: 5px;
}
.filter-blocked-select-box {
width: 184px;
}
}
@media (max-width: 1200px) {
.filter-tab {
padding: 6px 10px;
font-size: 0.74rem;
gap: 4px;
}
.filter-blocked-select-box {
width: 176px;
}
}
.filter-tabs { display: flex; gap: 4px; padding: 4px; background: rgba(255, 255, 255, 0.6); border: 1px solid rgba(17, 18, 20, 0.08); border-radius: 12px; backdrop-filter: blur(8px); }
.filter-tab { border: none; background: transparent; padding: 8px 16px; border-radius: 8px; font-size: 0.85rem; font-weight: 700; color: var(--muted); transition: all 0.2s ease; display: flex; align-items: center; gap: 6px; &:hover { color: var(--text); background: rgba(255, 255, 255, 0.5); } &.active { background: #fff; color: var(--brand); box-shadow: 0 2px 8px rgba(227, 61, 207, 0.15); } &:disabled { opacity: 0.5; cursor: not-allowed; } }
.client-filter-wrap { position: relative; z-index: 40; }
.btn-client-filter { display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 12px; border: 1px solid rgba(17, 18, 20, 0.08); background: rgba(255, 255, 255, 0.6); color: var(--muted); font-weight: 700; font-size: 0.85rem; backdrop-filter: blur(8px); transition: all 0.2s; min-height: 38px; height: auto; flex-wrap: wrap; &:hover { background: #fff; border-color: var(--blue); color: var(--blue); } &.active, &.has-selection { background: #fff; border-color: var(--brand); } }
@ -410,32 +335,17 @@
/* Controls */
.controls { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.controls-right {
margin-left: auto;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
flex-wrap: wrap;
@media (max-width: 900px) {
width: 100%;
justify-content: space-between;
}
@media (max-width: 500px) {
gap: 8px;
}
}
.search-group { max-width: 270px; border-radius: 12px; overflow: hidden; display: flex; align-items: stretch; background: #fff; border: 1px solid rgba(17, 18, 20, 0.15); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04); transition: all 0.2s ease; &:focus-within { border-color: var(--brand); box-shadow: 0 4px 12px rgba(227, 61, 207, 0.15); transform: translateY(-1px); } .input-group-text { background: transparent; border: none; color: var(--muted); padding-left: 14px; padding-right: 8px; display: flex; align-items: center; i { font-size: 1rem; } } .form-control { border: none; background: transparent; padding: 10px 0; font-size: 0.9rem; color: var(--text); box-shadow: none; &::placeholder { color: rgba(17, 18, 20, 0.4); font-weight: 500; } &:focus { outline: none; } } .btn-clear { background: transparent; border: none; color: var(--muted); padding: 0 12px; display: flex; align-items: center; cursor: pointer; transition: color 0.2s; &:hover { color: #dc3545; } i { font-size: 1rem; } } }
.page-size { @media (max-width: 500px) { width: 100%; justify-content: space-between; } }
.page-size { margin-left: auto; @media (max-width: 500px) { margin-left: 0; width: 100%; justify-content: space-between; } }
.batch-status-tools {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-left: auto;
@media (max-width: 900px) {
margin-left: 0;
width: 100%;
}
}

View File

@ -144,23 +144,6 @@ describe('Geral', () => {
expect(filtered[0].status).toBe('ATIVO');
});
it('should filter only pre-activation blocked lines when PRE_ATIVACAO is selected', () => {
component.filterStatus = 'BLOCKED';
component.blockedStatusMode = 'PRE_ATIVACAO';
component.filterOperadora = 'ALL';
component.filterContaEmpresa = '';
component.additionalMode = 'ALL';
component.selectedAdditionalServices = [];
const filtered = (component as any).applyAdditionalFiltersClientSide([
{ id: '1', item: 1, conta: '1', linha: '11911111111', cliente: 'A', usuario: 'U', vencConta: null, status: 'BLOQUEIO DE PRE ATIVACAO' },
{ id: '2', item: 2, conta: '2', linha: '11922222222', cliente: 'B', usuario: 'U', vencConta: null, status: 'BLOQUEIO 120 DIAS' },
]);
expect(filtered.length).toBe(1);
expect(filtered[0].status).toBe('BLOQUEIO DE PRE ATIVACAO');
});
it('should classify active line as ACTIVE during smart search resolution', () => {
const target = (component as any).buildSmartSearchTarget({
id: '1',
@ -177,33 +160,6 @@ describe('Geral', () => {
expect(target?.statusFilter).toBe('ACTIVE');
});
it('should classify pre-activation line as blocked during smart search resolution', () => {
const target = (component as any).buildSmartSearchTarget({
id: '1',
item: 1,
conta: '1',
linha: '11911111111',
cliente: 'CLIENTE A',
usuario: 'USUARIO A',
vencConta: null,
status: 'BLOQUEIO DE PRE ATIVACAO',
skil: 'PESSOA JURIDICA',
}, true);
expect(target?.statusFilter).toBe('BLOCKED');
expect(target?.blockedStatusMode).toBe('PRE_ATIVACAO');
});
it('should toggle blocked select filter off when selecting the same blocked option again', () => {
component.setBlockedStatusFilter('BLOQUEIO_120');
expect(component.filterStatus).toBe('BLOCKED');
expect(component.blockedStatusMode).toBe('BLOQUEIO_120');
component.setBlockedStatusFilter('BLOQUEIO_120');
expect(component.filterStatus).toBe('ALL');
expect(component.blockedStatusMode).toBe('ALL');
});
it('should request assigned reserve lines in ALL filter only', () => {
component.filterSkil = 'ALL';
let params = (component as any).applyBaseFilters(new HttpParams());

View File

@ -68,7 +68,7 @@ type CreateEntryMode = 'SINGLE' | 'BATCH';
type AdditionalMode = 'ALL' | 'WITH' | 'WITHOUT';
type OperadoraFilterMode = 'ALL' | 'VIVO' | 'CLARO' | 'TIM';
type AdditionalServiceKey = 'gvd' | 'skeelo' | 'news' | 'travel' | 'sync' | 'dispositivo';
type BlockedStatusMode = 'ALL' | 'PERDA_ROUBO' | 'BLOQUEIO_120' | 'PRE_ATIVACAO';
type BlockedStatusMode = 'ALL' | 'PERDA_ROUBO' | 'BLOQUEIO_120';
type SkilFilterMode = 'ALL' | 'PF' | 'PJ' | 'RESERVA' | 'ESTOQUE';
interface LineRow {
@ -418,12 +418,6 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
selectedAdditionalServices: AdditionalServiceKey[] = [];
filterOperadora: OperadoraFilterMode = 'ALL';
filterContaEmpresa = '';
readonly blockedStatusFilterOptions: Array<{ label: string; value: BlockedStatusMode }> = [
{ label: 'Bloqueadas', value: 'ALL' },
{ label: 'Bloqueio Perda/Roubo', value: 'PERDA_ROUBO' },
{ label: 'Bloqueio por 120 dias', value: 'BLOQUEIO_120' },
{ label: 'Bloqueio de Pré Ativação', value: 'PRE_ATIVACAO' },
];
readonly additionalServiceOptions: Array<{ key: AdditionalServiceKey; label: string }> = [
{ key: 'gvd', label: 'Gestão Voz e Dados' },
{ key: 'skeelo', label: 'Skeelo' },
@ -539,7 +533,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
kpiAtivas = 0;
kpiBloqueadas = 0;
readonly statusOptions = ['ATIVO', 'BLOQUEIO PERDA/ROUBO', 'BLOQUEIO 120 DIAS', 'BLOQUEIO DE PRÉ ATIVAÇÃO'];
readonly statusOptions = ['ATIVO', 'BLOQUEIO PERDA/ROUBO', 'BLOQUEIO 120 DIAS'];
readonly skilOptions = ['PESSOA FÍSICA', 'PESSOA JURÍDICA', 'RESERVA'];
planOptions = [
@ -1220,13 +1214,6 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
) {
return 'BLOQUEIO_120';
}
if (
token === 'PREATIVACAO' ||
token === 'PREATIV' ||
token === 'BLOQUEIOPREATIVACAO'
) {
return 'PRE_ATIVACAO';
}
return null;
}
@ -1858,18 +1845,13 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
this.refreshData();
}
setBlockedStatusFilter(mode: BlockedStatusMode) {
const normalizedMode = mode ?? 'ALL';
const sameSelection = this.filterStatus === 'BLOCKED' && this.blockedStatusMode === normalizedMode;
if (sameSelection) {
toggleBlockedFilter() {
if (this.filterStatus === 'BLOCKED') {
this.filterStatus = 'ALL';
this.blockedStatusMode = 'ALL';
} else {
this.filterStatus = 'BLOCKED';
this.blockedStatusMode = normalizedMode;
}
this.expandedGroup = null;
this.groupLines = [];
this.searchResolvedClient = null;
@ -1884,10 +1866,24 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
this.refreshData();
}
onBlockedStatusSelectChange(event: Event) {
const target = event.target;
const value = target instanceof HTMLSelectElement ? target.value : 'ALL';
this.setBlockedStatusFilter(value as BlockedStatusMode);
setBlockedStatusMode(mode: Exclude<BlockedStatusMode, 'ALL'>) {
if (this.filterStatus !== 'BLOCKED') {
this.filterStatus = 'BLOCKED';
}
this.blockedStatusMode = this.blockedStatusMode === mode ? 'ALL' : mode;
this.expandedGroup = null;
this.groupLines = [];
this.searchResolvedClient = null;
this.selectedClients = [];
this.clientSearchTerm = '';
this.page = 1;
if (!this.isClientRestricted) {
this.loadClients();
}
this.refreshData();
}
setAdditionalMode(mode: AdditionalMode) {
@ -1978,7 +1974,6 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
next = next.set('statusMode', 'blocked');
if (this.blockedStatusMode === 'PERDA_ROUBO') next = next.set('statusSubtype', 'perda_roubo');
else if (this.blockedStatusMode === 'BLOQUEIO_120') next = next.set('statusSubtype', '120_dias');
else if (this.blockedStatusMode === 'PRE_ATIVACAO') next = next.set('statusSubtype', 'pre_ativacao');
}
if (this.additionalMode === 'WITH') next = next.set('additionalMode', 'with');
@ -2025,17 +2020,15 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
normalized.includes('BLOQUE') ||
normalized.includes('PERDA') ||
normalized.includes('ROUBO') ||
normalized.includes('FURTO') ||
normalized.includes('PREATIV');
normalized.includes('FURTO');
if (!hasBlockedToken) return null;
if (normalized.includes('120')) return 'BLOQUEIO_120';
if (normalized.includes('PREATIV')) return 'PRE_ATIVACAO';
if (normalized.includes('PERDA') || normalized.includes('ROUBO') || normalized.includes('FURTO')) {
return 'PERDA_ROUBO';
}
return 'PRE_ATIVACAO';
return 'PERDA_ROUBO';
}
private isBlockedStatus(status: unknown): boolean {
@ -2946,7 +2939,6 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
} else if (this.filterStatus === 'BLOCKED') {
if (this.blockedStatusMode === 'PERDA_ROUBO') parts.push('bloq-perda-roubo');
else if (this.blockedStatusMode === 'BLOQUEIO_120') parts.push('bloq-120');
else if (this.blockedStatusMode === 'PRE_ATIVACAO') parts.push('bloq-pre-ativacao');
else parts.push('bloqueadas');
}