From d1ec70cd69d496ae8f6ac2a3035174e131d6cae6 Mon Sep 17 00:00:00 2001 From: Eduardo Lopes Date: Thu, 12 Mar 2026 16:08:17 -0300 Subject: [PATCH] Ajusta filtros e layout da pagina geral --- .../custom-select/custom-select.html | 2 +- .../custom-select/custom-select.scss | 3 +- src/app/pages/geral/geral.html | 88 +++++++-------- src/app/pages/geral/geral.scss | 100 +++++++++++++++++- src/app/pages/geral/geral.spec.ts | 44 ++++++++ src/app/pages/geral/geral.ts | 56 +++++----- 6 files changed, 214 insertions(+), 79 deletions(-) diff --git a/src/app/components/custom-select/custom-select.html b/src/app/components/custom-select/custom-select.html index 6fd6bb0..6b77859 100644 --- a/src/app/components/custom-select/custom-select.html +++ b/src/app/components/custom-select/custom-select.html @@ -7,7 +7,7 @@ [attr.aria-disabled]="disabled" > {{ displayLabel }} - +
diff --git a/src/app/components/custom-select/custom-select.scss b/src/app/components/custom-select/custom-select.scss index 4ffab52..117324d 100644 --- a/src/app/components/custom-select/custom-select.scss +++ b/src/app/components/custom-select/custom-select.scss @@ -75,8 +75,7 @@ white-space: nowrap; } - -.app-select-trigger i { +.app-select-trigger .app-select-chevron { position: absolute; right: 8px; top: 50%; diff --git a/src/app/pages/geral/geral.html b/src/app/pages/geral/geral.html index 0317570..796667f 100644 --- a/src/app/pages/geral/geral.html +++ b/src/app/pages/geral/geral.html @@ -98,27 +98,19 @@ - - - - - +
+ + +
@@ -347,34 +339,36 @@ -
- - Itens por pág: - - -
- +
+
+ + Selecionadas: {{ batchStatusSelectionCount }} + + +
-
-
- - Selecionadas: {{ batchStatusSelectionCount }} - - - +
+ + Itens por pág: + + +
+ +
+
diff --git a/src/app/pages/geral/geral.scss b/src/app/pages/geral/geral.scss index 2056380..6519cc5 100644 --- a/src/app/pages/geral/geral.scss +++ b/src/app/pages/geral/geral.scss @@ -143,13 +143,88 @@ .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); } } @@ -335,17 +410,32 @@ /* 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 { margin-left: auto; @media (max-width: 500px) { margin-left: 0; width: 100%; justify-content: space-between; } } +.page-size { @media (max-width: 500px) { 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%; } } diff --git a/src/app/pages/geral/geral.spec.ts b/src/app/pages/geral/geral.spec.ts index 4f94d6b..8cc0768 100644 --- a/src/app/pages/geral/geral.spec.ts +++ b/src/app/pages/geral/geral.spec.ts @@ -144,6 +144,23 @@ 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', @@ -160,6 +177,33 @@ 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()); diff --git a/src/app/pages/geral/geral.ts b/src/app/pages/geral/geral.ts index 06efc75..3ccfba5 100644 --- a/src/app/pages/geral/geral.ts +++ b/src/app/pages/geral/geral.ts @@ -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'; +type BlockedStatusMode = 'ALL' | 'PERDA_ROUBO' | 'BLOQUEIO_120' | 'PRE_ATIVACAO'; type SkilFilterMode = 'ALL' | 'PF' | 'PJ' | 'RESERVA' | 'ESTOQUE'; interface LineRow { @@ -418,6 +418,12 @@ 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' }, @@ -533,7 +539,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { kpiAtivas = 0; kpiBloqueadas = 0; - readonly statusOptions = ['ATIVO', 'BLOQUEIO PERDA/ROUBO', 'BLOQUEIO 120 DIAS']; + readonly statusOptions = ['ATIVO', 'BLOQUEIO PERDA/ROUBO', 'BLOQUEIO 120 DIAS', 'BLOQUEIO DE PRÉ ATIVAÇÃO']; readonly skilOptions = ['PESSOA FÍSICA', 'PESSOA JURÍDICA', 'RESERVA']; planOptions = [ @@ -1214,6 +1220,13 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { ) { return 'BLOQUEIO_120'; } + if ( + token === 'PREATIVACAO' || + token === 'PREATIV' || + token === 'BLOQUEIOPREATIVACAO' + ) { + return 'PRE_ATIVACAO'; + } return null; } @@ -1845,13 +1858,18 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { this.refreshData(); } - toggleBlockedFilter() { - if (this.filterStatus === 'BLOCKED') { + setBlockedStatusFilter(mode: BlockedStatusMode) { + const normalizedMode = mode ?? 'ALL'; + const sameSelection = this.filterStatus === 'BLOCKED' && this.blockedStatusMode === normalizedMode; + + if (sameSelection) { this.filterStatus = 'ALL'; this.blockedStatusMode = 'ALL'; } else { this.filterStatus = 'BLOCKED'; + this.blockedStatusMode = normalizedMode; } + this.expandedGroup = null; this.groupLines = []; this.searchResolvedClient = null; @@ -1866,24 +1884,10 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { this.refreshData(); } - setBlockedStatusMode(mode: Exclude) { - 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(); + onBlockedStatusSelectChange(event: Event) { + const target = event.target; + const value = target instanceof HTMLSelectElement ? target.value : 'ALL'; + this.setBlockedStatusFilter(value as BlockedStatusMode); } setAdditionalMode(mode: AdditionalMode) { @@ -1974,6 +1978,7 @@ 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'); @@ -2020,15 +2025,17 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { normalized.includes('BLOQUE') || normalized.includes('PERDA') || normalized.includes('ROUBO') || - normalized.includes('FURTO'); + normalized.includes('FURTO') || + normalized.includes('PREATIV'); 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 'PERDA_ROUBO'; + return 'PRE_ATIVACAO'; } private isBlockedStatus(status: unknown): boolean { @@ -2939,6 +2946,7 @@ 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'); }