Feat: Corrigindo merge

This commit is contained in:
Eduardo 2026-02-27 16:34:54 -03:00
parent 43efc1dc85
commit 4dcbfadd2c
23 changed files with 133 additions and 103 deletions

View File

@ -8,8 +8,8 @@ import { Mureg } from './pages/mureg/mureg';
import { Faturamento } from './pages/faturamento/faturamento'; import { Faturamento } from './pages/faturamento/faturamento';
import { authGuard } from './guards/auth.guard'; import { authGuard } from './guards/auth.guard';
import { adminGuard } from './guards/admin.guard'; import { sysadminOrGestorGuard } from './guards/sysadmin-or-gestor.guard';
import { systemAdminGuard } from './guards/system-admin.guard'; import { sysadminOnlyGuard } from './guards/sysadmin-only.guard';
import { DadosUsuarios } from './pages/dados-usuarios/dados-usuarios'; import { DadosUsuarios } from './pages/dados-usuarios/dados-usuarios';
import { VigenciaComponent } from './pages/vigencia/vigencia'; import { VigenciaComponent } from './pages/vigencia/vigencia';
import { TrocaNumero } from './pages/troca-numero/troca-numero'; import { TrocaNumero } from './pages/troca-numero/troca-numero';
@ -29,20 +29,20 @@ export const routes: Routes = [
{ path: 'geral', component: Geral, canActivate: [authGuard], title: 'Geral' }, { path: 'geral', component: Geral, canActivate: [authGuard], title: 'Geral' },
{ path: 'mureg', component: Mureg, canActivate: [authGuard], title: 'Mureg' }, { path: 'mureg', component: Mureg, canActivate: [authGuard], title: 'Mureg' },
{ path: 'faturamento', component: Faturamento, canActivate: [authGuard, adminGuard], title: 'Faturamento' }, { path: 'faturamento', component: Faturamento, canActivate: [authGuard, sysadminOrGestorGuard], title: 'Faturamento' },
{ path: 'dadosusuarios', component: DadosUsuarios, canActivate: [authGuard], title: 'Dados dos Usuários' }, { path: 'dadosusuarios', component: DadosUsuarios, canActivate: [authGuard], title: 'Dados dos Usuários' },
{ path: 'vigencia', component: VigenciaComponent, canActivate: [authGuard], title: 'Vigência' }, { path: 'vigencia', component: VigenciaComponent, canActivate: [authGuard], title: 'Vigência' },
{ path: 'trocanumero', component: TrocaNumero, canActivate: [authGuard], title: 'Troca de Número' }, { path: 'trocanumero', component: TrocaNumero, canActivate: [authGuard], title: 'Troca de Número' },
{ path: 'notificacoes', component: Notificacoes, canActivate: [authGuard], title: 'Notificações' }, { path: 'notificacoes', component: Notificacoes, canActivate: [authGuard], title: 'Notificações' },
{ path: 'chips-controle-recebidos', component: ChipsControleRecebidos, canActivate: [authGuard, adminGuard], title: 'Chips Controle Recebidos' }, { path: 'chips-controle-recebidos', component: ChipsControleRecebidos, canActivate: [authGuard, sysadminOrGestorGuard], title: 'Chips Controle Recebidos' },
{ path: 'resumo', component: Resumo, canActivate: [authGuard], title: 'Resumo' }, { path: 'resumo', component: Resumo, canActivate: [authGuard], title: 'Resumo' },
{ path: 'parcelamentos', component: Parcelamentos, canActivate: [authGuard, adminGuard], title: 'Parcelamentos' }, { path: 'parcelamentos', component: Parcelamentos, canActivate: [authGuard, sysadminOrGestorGuard], title: 'Parcelamentos' },
{ path: 'historico', component: Historico, canActivate: [authGuard, adminGuard], title: 'Histórico' }, { path: 'historico', component: Historico, canActivate: [authGuard, sysadminOrGestorGuard], title: 'Histórico' },
{ path: 'perfil', component: Perfil, canActivate: [authGuard], title: 'Perfil' }, { path: 'perfil', component: Perfil, canActivate: [authGuard], title: 'Perfil' },
{ {
path: 'system/fornecer-usuario', path: 'system/fornecer-usuario',
component: SystemProvisionUserPage, component: SystemProvisionUserPage,
canActivate: [authGuard, systemAdminGuard], canActivate: [authGuard, sysadminOnlyGuard],
title: 'Fornecer Usuário', title: 'Fornecer Usuário',
}, },

View File

@ -185,13 +185,13 @@
<i class="bi bi-person-circle"></i> Perfil <i class="bi bi-person-circle"></i> Perfil
</button> </button>
<div class="divider"></div> <div class="divider"></div>
<button type="button" class="options-item" *ngIf="isAdmin" (click)="openCreateUserModal()"> <button type="button" class="options-item" *ngIf="isSysAdmin" (click)="openCreateUserModal()">
<i class="bi bi-person-plus"></i> Criar novo usuário <i class="bi bi-person-plus"></i> Criar novo usuário
</button> </button>
<button type="button" class="options-item" *ngIf="isAdmin" (click)="openManageUsersModal()"> <button type="button" class="options-item" *ngIf="isSysAdmin" (click)="openManageUsersModal()">
<i class="bi bi-people"></i> Editar usuário <i class="bi bi-people"></i> Editar usuário
</button> </button>
<button type="button" class="options-item" *ngIf="isSystemAdmin" (click)="goToSystemProvisionUser()"> <button type="button" class="options-item" *ngIf="isSysAdmin" (click)="goToSystemProvisionUser()">
<i class="bi bi-shield-lock"></i> Fornecer usuário (cliente) <i class="bi bi-shield-lock"></i> Fornecer usuário (cliente)
</button> </button>
<div class="divider"></div> <div class="divider"></div>
@ -537,7 +537,7 @@
<a *ngIf="canViewAll" routerLink="/trocanumero" routerLinkActive="active" class="side-item" (click)="closeMenu()"> <a *ngIf="canViewAll" routerLink="/trocanumero" routerLinkActive="active" class="side-item" (click)="closeMenu()">
<i class="bi bi-arrow-left-right"></i> <span>Troca de número</span> <i class="bi bi-arrow-left-right"></i> <span>Troca de número</span>
</a> </a>
<a *ngIf="isSystemAdmin" routerLink="/system/fornecer-usuario" routerLinkActive="active" class="side-item" (click)="closeMenu()"> <a *ngIf="isSysAdmin" routerLink="/system/fornecer-usuario" routerLinkActive="active" class="side-item" (click)="closeMenu()">
<i class="bi bi-shield-lock-fill"></i> <span>Fornecer usuário</span> <i class="bi bi-shield-lock-fill"></i> <span>Fornecer usuário</span>
</a> </a>
</div> </div>

View File

@ -31,9 +31,8 @@ export class Header implements AfterViewInit, OnDestroy {
manageUsersOpen = false; manageUsersOpen = false;
isLoggedHeader = false; isLoggedHeader = false;
isHome = false; isHome = false;
isAdmin = false; isSysAdmin = false;
canViewAll = false; canViewAll = false;
isSystemAdmin = false;
notifications: NotificationDto[] = []; notifications: NotificationDto[] = [];
notificationsLoading = false; notificationsLoading = false;
notificationsError = false; notificationsError = false;
@ -203,16 +202,14 @@ export class Header implements AfterViewInit, OnDestroy {
private syncPermissions() { private syncPermissions() {
if (!isPlatformBrowser(this.platformId)) { if (!isPlatformBrowser(this.platformId)) {
this.isAdmin = false; this.isSysAdmin = false;
this.canViewAll = false; this.canViewAll = false;
this.isSystemAdmin = false;
return; return;
} }
const isSysAdmin = this.authService.hasRole('sysadmin'); const isSysAdmin = this.authService.hasRole('sysadmin');
const isGestor = this.authService.hasRole('gestor'); const isGestor = this.authService.hasRole('gestor');
this.isAdmin = isSysAdmin; this.isSysAdmin = isSysAdmin;
this.canViewAll = isSysAdmin || isGestor; this.canViewAll = isSysAdmin || isGestor;
this.isSystemAdmin = this.authService.hasRole('sysadmin');
} }
toggleMenu() { toggleMenu() {
@ -238,13 +235,13 @@ export class Header implements AfterViewInit, OnDestroy {
} }
goToSystemProvisionUser() { goToSystemProvisionUser() {
if (!this.isSystemAdmin) return; if (!this.isSysAdmin) return;
this.closeOptions(); this.closeOptions();
this.router.navigate(['/system/fornecer-usuario']); this.router.navigate(['/system/fornecer-usuario']);
} }
openCreateUserModal() { openCreateUserModal() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.createUserOpen = true; this.createUserOpen = true;
this.closeOptions(); this.closeOptions();
this.resetCreateUserState(); this.resetCreateUserState();
@ -256,7 +253,7 @@ export class Header implements AfterViewInit, OnDestroy {
} }
openManageUsersModal() { openManageUsersModal() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.manageUsersOpen = true; this.manageUsersOpen = true;
this.closeOptions(); this.closeOptions();
this.resetManageUsersState(); this.resetManageUsersState();
@ -452,9 +449,8 @@ export class Header implements AfterViewInit, OnDestroy {
this.authService.logout(); this.authService.logout();
this.optionsOpen = false; this.optionsOpen = false;
this.notificationsOpen = false; this.notificationsOpen = false;
this.isAdmin = false; this.isSysAdmin = false;
this.canViewAll = false; this.canViewAll = false;
this.isSystemAdmin = false;
this.router.navigate(['/']); this.router.navigate(['/']);
} }
@ -616,7 +612,7 @@ export class Header implements AfterViewInit, OnDestroy {
this.createUserForm.markAllAsTouched(); this.createUserForm.markAllAsTouched();
return; return;
} }
if (!this.isAdmin) { if (!this.isSysAdmin) {
this.createUserForbidden = true; this.createUserForbidden = true;
return; return;
} }

View File

@ -4,7 +4,7 @@ import { isPlatformBrowser } from '@angular/common';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
export const systemAdminGuard: CanActivateFn = () => { export const sysadminOnlyGuard: CanActivateFn = () => {
const router = inject(Router); const router = inject(Router);
const platformId = inject(PLATFORM_ID); const platformId = inject(PLATFORM_ID);
const authService = inject(AuthService); const authService = inject(AuthService);
@ -18,8 +18,8 @@ export const systemAdminGuard: CanActivateFn = () => {
return router.parseUrl('/login'); return router.parseUrl('/login');
} }
const isSystemAdmin = authService.hasRole('sysadmin'); const isSysAdmin = authService.hasRole('sysadmin');
if (!isSystemAdmin) { if (!isSysAdmin) {
return router.parseUrl('/dashboard'); return router.parseUrl('/dashboard');
} }

View File

@ -3,7 +3,7 @@ import { CanActivateFn, Router } from '@angular/router';
import { isPlatformBrowser } from '@angular/common'; import { isPlatformBrowser } from '@angular/common';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
export const adminGuard: CanActivateFn = () => { export const sysadminOrGestorGuard: CanActivateFn = () => {
const router = inject(Router); const router = inject(Router);
const platformId = inject(PLATFORM_ID); const platformId = inject(PLATFORM_ID);
const authService = inject(AuthService); const authService = inject(AuthService);

View File

@ -36,14 +36,14 @@
<div class="header-actions d-flex gap-2 justify-content-end"> <div class="header-actions d-flex gap-2 justify-content-end">
<button <button
*ngIf="isAdmin && activeTab === 'chips'" *ngIf="isSysAdmin && activeTab === 'chips'"
class="btn btn-brand btn-sm" class="btn btn-brand btn-sm"
(click)="openChipCreate()" (click)="openChipCreate()"
> >
<i class="bi bi-plus-circle me-1"></i> Novo Chip <i class="bi bi-plus-circle me-1"></i> Novo Chip
</button> </button>
<button <button
*ngIf="isAdmin && activeTab === 'controle'" *ngIf="isSysAdmin && activeTab === 'controle'"
class="btn btn-brand btn-sm" class="btn btn-brand btn-sm"
(click)="openControleCreate()" (click)="openControleCreate()"
> >
@ -197,10 +197,10 @@
<button class="btn-icon info" (click)="openChipDetail(r); $event.stopPropagation()" title="Detalhes"> <button class="btn-icon info" (click)="openChipDetail(r); $event.stopPropagation()" title="Detalhes">
<i class="bi bi-eye"></i> <i class="bi bi-eye"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="openChipEdit(r); $event.stopPropagation()" title="Editar"> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="openChipEdit(r); $event.stopPropagation()" title="Editar">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="openChipDelete(r); $event.stopPropagation()" title="Excluir"> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="openChipDelete(r); $event.stopPropagation()" title="Excluir">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
</div> </div>
@ -295,10 +295,10 @@
<button class="btn-icon info" (click)="openControleDetail(r); $event.stopPropagation()" title="Detalhes"> <button class="btn-icon info" (click)="openControleDetail(r); $event.stopPropagation()" title="Detalhes">
<i class="bi bi-eye"></i> <i class="bi bi-eye"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="openControleEdit(r); $event.stopPropagation()" title="Editar"> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="openControleEdit(r); $event.stopPropagation()" title="Editar">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="openControleDelete(r); $event.stopPropagation()" title="Excluir"> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="openControleDelete(r); $event.stopPropagation()" title="Excluir">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
</div> </div>
@ -339,10 +339,10 @@
<button class="btn-icon info" (click)="openControleDetail(r); $event.stopPropagation()" title="Detalhes"> <button class="btn-icon info" (click)="openControleDetail(r); $event.stopPropagation()" title="Detalhes">
<i class="bi bi-eye"></i> <i class="bi bi-eye"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="openControleEdit(r); $event.stopPropagation()" title="Editar"> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="openControleEdit(r); $event.stopPropagation()" title="Editar">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</button> </button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="openControleDelete(r); $event.stopPropagation()" title="Excluir"> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="openControleDelete(r); $event.stopPropagation()" title="Excluir">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
</div> </div>

View File

@ -118,7 +118,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
controleDeleteOpen = false; controleDeleteOpen = false;
controleDeleteTarget: ControleRecebidoListDto | null = null; controleDeleteTarget: ControleRecebidoListDto | null = null;
isAdmin = false; isSysAdmin = false;
constructor( constructor(
@Inject(PLATFORM_ID) private platformId: object, @Inject(PLATFORM_ID) private platformId: object,
@ -129,7 +129,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
if (!isPlatformBrowser(this.platformId)) return; if (!isPlatformBrowser(this.platformId)) return;
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
this.fetchChips(); this.fetchChips();
this.fetchControle(); this.fetchControle();
} }
@ -236,7 +236,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openChipCreate() { openChipCreate() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.chipCreateModel = { this.chipCreateModel = {
id: '', id: '',
item: null, item: null,
@ -278,7 +278,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openChipEdit(row: ChipVirgemListDto) { openChipEdit(row: ChipVirgemListDto) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.service.getChipVirgemById(row.id).subscribe({ this.service.getChipVirgemById(row.id).subscribe({
next: (data) => { next: (data) => {
this.chipEditingId = data.id; this.chipEditingId = data.id;
@ -319,7 +319,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openChipDelete(row: ChipVirgemListDto) { openChipDelete(row: ChipVirgemListDto) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.chipDeleteTarget = row; this.chipDeleteTarget = row;
this.chipDeleteOpen = true; this.chipDeleteOpen = true;
} }
@ -498,7 +498,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openControleCreate() { openControleCreate() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.controleCreateModel = { this.controleCreateModel = {
id: '', id: '',
ano: new Date().getFullYear(), ano: new Date().getFullYear(),
@ -603,7 +603,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openControleEdit(row: ControleRecebidoListDto) { openControleEdit(row: ControleRecebidoListDto) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.service.getControleRecebidoById(row.id).subscribe({ this.service.getControleRecebidoById(row.id).subscribe({
next: (data) => { next: (data) => {
this.controleEditingId = data.id; this.controleEditingId = data.id;
@ -659,7 +659,7 @@ export class ChipsControleRecebidos implements OnInit, OnDestroy {
} }
openControleDelete(row: ControleRecebidoListDto) { openControleDelete(row: ControleRecebidoListDto) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.controleDeleteTarget = row; this.controleDeleteTarget = row;
this.controleDeleteOpen = true; this.controleDeleteOpen = true;
} }

View File

@ -32,7 +32,7 @@
<button type="button" class="btn btn-brand btn-sm" (click)="refresh()" [disabled]="loading"> <button type="button" class="btn btn-brand btn-sm" (click)="refresh()" [disabled]="loading">
<i class="bi bi-arrow-clockwise me-1"></i> Atualizar <i class="bi bi-arrow-clockwise me-1"></i> Atualizar
</button> </button>
<button *ngIf="isAdmin" type="button" class="btn btn-brand btn-sm" (click)="openCreate()"> <button *ngIf="isSysAdmin" type="button" class="btn btn-brand btn-sm" (click)="openCreate()">
<i class="bi bi-plus-circle me-1"></i> Novo Usuário <i class="bi bi-plus-circle me-1"></i> Novo Usuário
</button> </button>
</div> </div>
@ -153,8 +153,8 @@
<td> <td>
<div class="action-group justify-content-center"> <div class="action-group justify-content-center">
<button class="btn-icon primary" (click)="openDetails(r)" title="Ver Detalhes"><i class="bi bi-eye"></i></button> <button class="btn-icon primary" (click)="openDetails(r)" title="Ver Detalhes"><i class="bi bi-eye"></i></button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="openEdit(r)" title="Editar"><i class="bi bi-pencil-square"></i></button> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="openEdit(r)" title="Editar"><i class="bi bi-pencil-square"></i></button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="openDelete(r)" title="Excluir"><i class="bi bi-trash"></i></button> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="openDelete(r)" title="Excluir"><i class="bi bi-trash"></i></button>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -103,7 +103,7 @@ export class DadosUsuarios implements OnInit {
createClientsLoading = false; createClientsLoading = false;
createLinesLoading = false; createLinesLoading = false;
isAdmin = false; isSysAdmin = false;
toastOpen = false; toastOpen = false;
toastMessage = ''; toastMessage = '';
toastType: 'success' | 'danger' = 'success'; toastType: 'success' | 'danger' = 'success';
@ -117,7 +117,7 @@ export class DadosUsuarios implements OnInit {
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
this.fetch(1); this.fetch(1);
} }
@ -283,7 +283,7 @@ export class DadosUsuarios implements OnInit {
closeDetails() { this.detailsOpen = false; } closeDetails() { this.detailsOpen = false; }
openEdit(row: UserDataRow) { openEdit(row: UserDataRow) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.service.getById(row.id).subscribe({ this.service.getById(row.id).subscribe({
next: (fullData: UserDataRow) => { next: (fullData: UserDataRow) => {
this.editingId = fullData.id; this.editingId = fullData.id;
@ -366,7 +366,7 @@ export class DadosUsuarios implements OnInit {
// CREATE // CREATE
// ========================== // ==========================
openCreate() { openCreate() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.resetCreateModel(); this.resetCreateModel();
this.createOpen = true; this.createOpen = true;
this.preloadGeralClients(); this.preloadGeralClients();
@ -532,7 +532,7 @@ export class DadosUsuarios implements OnInit {
} }
openDelete(row: UserDataRow) { openDelete(row: UserDataRow) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.deleteTarget = row; this.deleteTarget = row;
this.deleteOpen = true; this.deleteOpen = true;
} }

View File

@ -286,8 +286,8 @@
<div class="action-group justify-content-center"> <div class="action-group justify-content-center">
<button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button> <button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button>
<button class="btn-icon success" (click)="onComparativo(r)" title="Comparativo Vivo x Line"><i class="bi bi-columns-gap"></i></button> <button class="btn-icon success" (click)="onComparativo(r)" title="Comparativo Vivo x Line"><i class="bi bi-columns-gap"></i></button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="onDelete(r)" title="Excluir"><i class="bi bi-trash"></i></button> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="onDelete(r)" title="Excluir"><i class="bi bi-trash"></i></button>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -105,7 +105,7 @@ export class Faturamento implements AfterViewInit, OnDestroy {
deleteOpen = false; deleteOpen = false;
deleteTarget: BillingItem | null = null; deleteTarget: BillingItem | null = null;
isAdmin = false; isSysAdmin = false;
private searchTimer: any = null; private searchTimer: any = null;
private searchResolvedClients: string[] = []; private searchResolvedClients: string[] = [];
@ -164,7 +164,7 @@ export class Faturamento implements AfterViewInit, OnDestroy {
if (!isPlatformBrowser(this.platformId)) return; if (!isPlatformBrowser(this.platformId)) return;
this.initAnimations(); this.initAnimations();
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
setTimeout(() => { setTimeout(() => {
this.refreshData(true); this.refreshData(true);
@ -714,7 +714,7 @@ export class Faturamento implements AfterViewInit, OnDestroy {
} }
onEditar(r: BillingItem) { onEditar(r: BillingItem) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.editingId = r.id; this.editingId = r.id;
this.editModel = { ...r }; this.editModel = { ...r };
this.editOpen = true; this.editOpen = true;
@ -729,7 +729,7 @@ export class Faturamento implements AfterViewInit, OnDestroy {
} }
onDelete(r: BillingItem) { onDelete(r: BillingItem) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.deleteTarget = r; this.deleteTarget = r;
this.deleteOpen = true; this.deleteOpen = true;
this.cdr.detectChanges(); this.cdr.detectChanges();

View File

@ -35,7 +35,7 @@
<button <button
type="button" type="button"
class="btn btn-glass btn-sm" class="btn btn-glass btn-sm"
*ngIf="isAdmin" *ngIf="isSysAdmin"
(click)="onImportExcel()" (click)="onImportExcel()"
[disabled]="loading"> [disabled]="loading">
<i class="bi bi-file-earmark-excel me-1"></i> Importar Dados Excel <i class="bi bi-file-earmark-excel me-1"></i> Importar Dados Excel
@ -294,21 +294,49 @@
<div class="group-header" (click)="toggleGroup(group.cliente)"> <div class="group-header" (click)="toggleGroup(group.cliente)">
<div class="group-info"> <div class="group-info">
<h6 class="mb-0 fw-bold text-dark">{{ group.cliente }}</h6> <h6 class="mb-0 fw-bold text-dark">{{ group.cliente }}</h6>
<div class="group-tags"> <div class="group-tags">
<span class="tag-pill">{{ group.totalLinhas }} linhas</span> <span class="tag-pill">{{ group.totalLinhas }} linhas</span>
<span class="tag-pill active" *ngIf="group.ativos > 0">{{ group.ativos }} ativas</span> <span class="tag-pill active" *ngIf="group.ativos > 0">{{ group.ativos }} ativas</span>
<span class="tag-pill blocked" *ngIf="group.bloqueados > 0">{{ group.bloqueados }} bloqueadas</span> <span class="tag-pill blocked" *ngIf="group.bloqueados > 0">{{ group.bloqueados }} bloqueadas</span>
</div>
</div>
<div class="group-toggle-icon"><i class="bi bi-chevron-down"></i></div>
</div> </div>
</div>
<div class="group-toggle-icon"><i class="bi bi-chevron-down"></i></div>
</div>
<div class="group-body" *ngIf="expandedGroup === group.cliente"> <div class="group-body" *ngIf="expandedGroup === group.cliente">
<div class="d-flex justify-content-between align-items-center px-4 py-2 border-bottom bg-white"> <div class="d-flex justify-content-between align-items-center px-4 py-2 border-bottom bg-white">
<small class="text-muted fw-bold">Gerenciar Grupo</small> <small class="text-muted fw-bold">Gerenciar Grupo</small>
<button class="btn btn-sm btn-add-line-group" (click)="onAddLineToGroup(group.cliente)"> <div class="d-flex align-items-center gap-2 flex-wrap justify-content-end">
<i class="bi bi-plus-lg me-1"></i> Adicionar Linha <ng-container *ngIf="hasGroupLineSelectionTools">
</button> <button class="btn btn-sm btn-glass" type="button" (click)="toggleSelectAllReservaGroupLines()">
<i class="bi bi-check2-square me-1"></i>
{{ reservaSelectedCount > 0 && reservaSelectedCount === groupLines.length ? 'Limpar seleção' : 'Selecionar todas' }}
</button>
</ng-container>
<ng-container *ngIf="isReservaExpandedGroup">
<button
class="btn btn-sm btn-brand"
type="button"
(click)="openReservaTransferModal()"
[disabled]="reservaSelectedCount === 0"
>
<i class="bi bi-arrow-left-right me-1"></i> Atribuir Selecionadas ({{ reservaSelectedCount }})
</button>
</ng-container>
<ng-container *ngIf="canMoveSelectedLinesToReserva">
<button
class="btn btn-sm btn-send-reserva-group"
type="button"
(click)="openMoveToReservaModal()"
[disabled]="reservaSelectedCount === 0"
>
<i class="bi bi-box-arrow-left me-1"></i> Enviar p/ Reserva ({{ reservaSelectedCount }})
</button>
</ng-container>
<button class="btn btn-sm btn-add-line-group" *ngIf="!isClientRestricted" (click)="onAddLineToGroup(group.cliente)">
<i class="bi bi-plus-lg me-1"></i> Adicionar Linha
</button>
</div>
</div> </div>
<!-- ✅ wrapper com classe extra para permitir MAIS ALTURA em notebook/TV via SCSS --> <!-- ✅ wrapper com classe extra para permitir MAIS ALTURA em notebook/TV via SCSS -->
@ -334,7 +362,7 @@
<th>LINHA</th> <th>LINHA</th>
<th>USUÁRIO</th> <th>USUÁRIO</th>
<th>STATUS</th> <th>STATUS</th>
<th>VENCIMENTO</th> <th *ngIf="!isClientRestricted">VENCIMENTO</th>
<th>AÇÕES</th> <th>AÇÕES</th>
</tr> </tr>
</thead> </thead>
@ -351,23 +379,29 @@
[attr.aria-label]="'Selecionar linha ' + (r.linha || r.item)" [attr.aria-label]="'Selecionar linha ' + (r.linha || r.item)"
/> />
</td> </td>
<td class="text-muted fw-bold">{{ r.item }}</td> <td class="text-muted fw-bold">{{ r.item }}</td>
<td class="fw-black text-blue" [attr.title]="(r.chip || '') ? ('ICCID: ' + r.chip) : ''"> <td class="fw-black text-blue" [attr.title]="(r.chip || '') ? ('ICCID: ' + r.chip) : ''">
{{ r.linha }} {{ r.linha }}
<div class="small text-muted fw-normal" *ngIf="r.chip">ICCID: {{ r.chip }}</div> <div class="small text-muted fw-normal" *ngIf="r.chip">ICCID: {{ r.chip }}</div>
</td> </td>
<td class="text-dark">{{ r.usuario || '-' }}</td> <td class="text-dark">{{ r.usuario || '-' }}</td>
<td> <td>
<span class="status-pill" [ngClass]="statusClass(r.status)">{{ statusLabel(r.status) }}</span> <span class="status-pill" [ngClass]="statusClass(r.status)">{{ statusLabel(r.status) }}</span>
</td> </td>
<td class="text-muted small fw-bold">{{ r.contrato }}</td>
<td class="text-muted small fw-bold" *ngIf="!isClientRestricted">{{ r.contrato }}</td>
<td> <td>
<div class="action-group justify-content-center"> <div class="action-group justify-content-center">
<button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button> <button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button>
<ng-container *ngIf="!isClientRestricted"> <ng-container *ngIf="!isClientRestricted">
<button class="btn-icon success" (click)="onFinanceiro(r)" title="Financeiro"><i class="bi bi-cash-coin"></i></button> <button class="btn-icon success" (click)="onFinanceiro(r)" title="Financeiro"><i class="bi bi-cash-coin"></i></button>
<button class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button> <button class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="onRemover(r, true)" title="Remover"><i class="bi bi-trash"></i></button> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="onRemover(r, true)" title="Remover"><i class="bi bi-trash"></i></button>
</ng-container> </ng-container>
</div> </div>
</td> </td>
@ -479,14 +513,14 @@
<span class="status-pill" [ngClass]="statusClass(r.status)" [title]="r.status || ''">{{ statusLabel(r.status) }}</span> <span class="status-pill" [ngClass]="statusClass(r.status)" [title]="r.status || ''">{{ statusLabel(r.status) }}</span>
</td> </td>
<td class="text-center fw-bold text-muted small">{{ r.skil }}</td> <td class="text-center fw-bold text-muted small">{{ r.skil }}</td>
<td class="text-center fw-bold text-muted small">{{ r.contrato }}</td> <td class="text-center fw-bold text-muted small" *ngIf="!isClientRestricted">{{ r.contrato }}</td>
<td class="text-center"> <td class="text-center">
<div class="action-group justify-content-center"> <div class="action-group justify-content-center">
<button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button> <button class="btn-icon" (click)="onDetalhes(r)" title="Detalhes"><i class="bi bi-eye"></i></button>
<ng-container *ngIf="!isClientRestricted"> <ng-container *ngIf="!isClientRestricted">
<button class="btn-icon success" (click)="onFinanceiro(r)" title="Financeiro"><i class="bi bi-cash-coin"></i></button> <button class="btn-icon success" (click)="onFinanceiro(r)" title="Financeiro"><i class="bi bi-cash-coin"></i></button>
<button class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button> <button class="btn-icon primary" (click)="onEditar(r)" title="Editar"><i class="bi bi-pencil-square"></i></button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="onRemover(r)" title="Remover"><i class="bi bi-trash"></i></button> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="onRemover(r)" title="Remover"><i class="bi bi-trash"></i></button>
</ng-container> </ng-container>
</div> </div>
</td> </td>

View File

@ -274,7 +274,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
return `${apiBase}/templates`; return `${apiBase}/templates`;
})(); })();
loading = false; loading = false;
isAdmin = false; isSysAdmin = false;
isGestor = false; isGestor = false;
isClientRestricted = false; isClientRestricted = false;
@ -691,9 +691,9 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
if (!isPlatformBrowser(this.platformId)) return; if (!isPlatformBrowser(this.platformId)) return;
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
this.isGestor = this.authService.hasRole('gestor'); this.isGestor = this.authService.hasRole('gestor');
this.isClientRestricted = !(this.isAdmin || this.isGestor); this.isClientRestricted = !(this.isSysAdmin || this.isGestor);
if (this.isClientRestricted) { if (this.isClientRestricted) {
this.filterSkil = 'ALL'; this.filterSkil = 'ALL';
@ -1760,7 +1760,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
} }
async onImportExcel() { async onImportExcel() {
if (!this.isAdmin) { if (!this.isSysAdmin) {
await this.showToast('Você não tem permissão para importar planilha.'); await this.showToast('Você não tem permissão para importar planilha.');
return; return;
} }
@ -1771,7 +1771,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
} }
onExcelSelected(ev: Event) { onExcelSelected(ev: Event) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
const file = (ev.target as HTMLInputElement).files?.[0]; const file = (ev.target as HTMLInputElement).files?.[0];
if (!file) return; if (!file) return;
@ -1961,7 +1961,7 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy {
} }
async onRemover(r: LineRow, fromGroup = false) { async onRemover(r: LineRow, fromGroup = false) {
if (!this.isAdmin) { if (!this.isSysAdmin) {
await this.showToast('Apenas sysadmin pode remover linhas.'); await this.showToast('Apenas sysadmin pode remover linhas.');
return; return;
} }

View File

@ -21,7 +21,7 @@
type="email" type="email"
id="email" id="email"
formControlName="username" formControlName="username"
placeholder="admin@empresa.com" placeholder="usuario@empresa.com"
[class.error]="hasError('username')" [class.error]="hasError('username')"
> >
<div class="error-msg" *ngIf="hasError('username')">E-mail obrigatório ou inválido.</div> <div class="error-msg" *ngIf="hasError('username')">E-mail obrigatório ou inválido.</div>

View File

@ -113,7 +113,7 @@
type="button" type="button"
title="Excluir" title="Excluir"
aria-label="Excluir" aria-label="Excluir"
*ngIf="isAdmin" *ngIf="isSysAdmin"
(click)="remove.emit(row)"> (click)="remove.emit(row)">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>

View File

@ -26,7 +26,7 @@ export class ParcelamentosTableComponent {
@Input() items: ParcelamentoViewItem[] = []; @Input() items: ParcelamentoViewItem[] = [];
@Input() loading = false; @Input() loading = false;
@Input() errorMessage = ''; @Input() errorMessage = '';
@Input() isAdmin = false; @Input() isSysAdmin = false;
@Input() segment: ParcelamentoSegment = 'todos'; @Input() segment: ParcelamentoSegment = 'todos';
@Input() segmentCounts: Record<ParcelamentoSegment, number> = { @Input() segmentCounts: Record<ParcelamentoSegment, number> = {

View File

@ -65,7 +65,7 @@
[total]="total" [total]="total"
[pageSize]="pageSize" [pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions" [pageSizeOptions]="pageSizeOptions"
[isAdmin]="isAdmin" [isSysAdmin]="isSysAdmin"
(segmentChange)="setSegment($event)" (segmentChange)="setSegment($event)"
(detail)="openDetails($event)" (detail)="openDetails($event)"
(edit)="openEdit($event)" (edit)="openEdit($event)"

View File

@ -87,7 +87,7 @@ export class Parcelamentos implements OnInit, OnDestroy {
kpiCards: ParcelamentoKpi[] = []; kpiCards: ParcelamentoKpi[] = [];
activeChips: FilterChip[] = []; activeChips: FilterChip[] = [];
isAdmin = false; isSysAdmin = false;
detailOpen = false; detailOpen = false;
detailLoading = false; detailLoading = false;
@ -158,7 +158,7 @@ export class Parcelamentos implements OnInit, OnDestroy {
} }
private syncPermissions(): void { private syncPermissions(): void {
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
} }
get totalPages(): number { get totalPages(): number {
@ -440,7 +440,7 @@ export class Parcelamentos implements OnInit, OnDestroy {
} }
openDelete(item: ParcelamentoViewItem): void { openDelete(item: ParcelamentoViewItem): void {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.deleteTarget = item; this.deleteTarget = item;
this.deleteError = ''; this.deleteError = '';
this.deleteOpen = true; this.deleteOpen = true;

View File

@ -7,7 +7,7 @@
<div class="card-shell"> <div class="card-shell">
<header class="card-header"> <header class="card-header">
<div class="title-badge"> <div class="title-badge">
<i class="bi bi-shield-lock-fill"></i> SYSTEM ADMIN <i class="bi bi-shield-lock-fill"></i> SYSADMIN
</div> </div>
<h1>Fornecer Usuário para Cliente</h1> <h1>Fornecer Usuário para Cliente</h1>
<p>Selecione um tenant-cliente e crie credenciais de acesso sem misturar tenants.</p> <p>Selecione um tenant-cliente e crie credenciais de acesso sem misturar tenants.</p>

View File

@ -11,10 +11,10 @@ import {
} from '@angular/forms'; } from '@angular/forms';
import { import {
SystemAdminService, SysadminService,
SystemTenantDto, SystemTenantDto,
CreateSystemTenantUserResponse, CreateSystemTenantUserResponse,
} from '../../services/system-admin.service'; } from '../../services/sysadmin.service';
type RoleOption = { type RoleOption = {
value: string; value: string;
@ -50,7 +50,7 @@ export class SystemProvisionUserPage implements OnInit {
constructor( constructor(
private fb: FormBuilder, private fb: FormBuilder,
private systemAdminService: SystemAdminService private sysadminService: SysadminService
) { ) {
this.provisionForm = this.fb.group( this.provisionForm = this.fb.group(
{ {
@ -75,7 +75,7 @@ export class SystemProvisionUserPage implements OnInit {
this.tenantsLoading = true; this.tenantsLoading = true;
this.tenantsError = ''; this.tenantsError = '';
this.systemAdminService this.sysadminService
.listTenants({ source: this.sourceType, active: true }) .listTenants({ source: this.sourceType, active: true })
.subscribe({ .subscribe({
next: (tenants) => { next: (tenants) => {
@ -133,7 +133,7 @@ export class SystemProvisionUserPage implements OnInit {
this.submitting = true; this.submitting = true;
this.setFormDisabled(true); this.setFormDisabled(true);
this.systemAdminService this.sysadminService
.createTenantUser(tenantId, { .createTenantUser(tenantId, {
name: nameRaw, name: nameRaw,
email, email,

View File

@ -24,7 +24,7 @@
<small class="subtitle">Controle de contratos e fidelização</small> <small class="subtitle">Controle de contratos e fidelização</small>
</div> </div>
<div class="header-actions d-flex gap-2 justify-content-end"> <div class="header-actions d-flex gap-2 justify-content-end">
<button *ngIf="isAdmin" class="btn btn-brand btn-sm" (click)="openCreate()"> <button *ngIf="isSysAdmin" class="btn btn-brand btn-sm" (click)="openCreate()">
<i class="bi bi-plus-circle me-1"></i> Nova Vigência <i class="bi bi-plus-circle me-1"></i> Nova Vigência
</button> </button>
</div> </div>
@ -157,8 +157,8 @@
Renovar +2 anos Renovar +2 anos
</button> </button>
<button class="btn-icon primary" (click)="openDetails(row)" title="Ver Detalhes"><i class="bi bi-eye"></i></button> <button class="btn-icon primary" (click)="openDetails(row)" title="Ver Detalhes"><i class="bi bi-eye"></i></button>
<button *ngIf="isAdmin" class="btn-icon primary" (click)="openEdit(row)" title="Editar"><i class="bi bi-pencil-square"></i></button> <button *ngIf="isSysAdmin" class="btn-icon primary" (click)="openEdit(row)" title="Editar"><i class="bi bi-pencil-square"></i></button>
<button *ngIf="isAdmin" class="btn-icon danger" (click)="openDelete(row)" title="Excluir"><i class="bi bi-trash"></i></button> <button *ngIf="isSysAdmin" class="btn-icon danger" (click)="openDelete(row)" title="Excluir"><i class="bi bi-trash"></i></button>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -100,7 +100,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
clientsFromGeral: string[] = []; clientsFromGeral: string[] = [];
planOptions: string[] = []; planOptions: string[] = [];
isAdmin = false; isSysAdmin = false;
toastOpen = false; toastOpen = false;
toastMessage = ''; toastMessage = '';
toastType: ToastType = 'success'; toastType: ToastType = 'success';
@ -117,7 +117,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.isAdmin = this.authService.hasRole('sysadmin'); this.isSysAdmin = this.authService.hasRole('sysadmin');
this.loadClients(); this.loadClients();
this.loadPlanRules(); this.loadPlanRules();
this.fetch(1); this.fetch(1);
@ -314,7 +314,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
closeDetails() { this.detailsOpen = false; } closeDetails() { this.detailsOpen = false; }
openEdit(r: VigenciaRow) { openEdit(r: VigenciaRow) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.editingId = r.id; this.editingId = r.id;
this.editModel = { ...r }; this.editModel = { ...r };
this.editEfetivacao = this.toDateInput(r.dtEfetivacaoServico); this.editEfetivacao = this.toDateInput(r.dtEfetivacaoServico);
@ -365,7 +365,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
// CREATE // CREATE
// ========================== // ==========================
openCreate() { openCreate() {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.resetCreateModel(); this.resetCreateModel();
this.createOpen = true; this.createOpen = true;
this.preloadGeralClients(); this.preloadGeralClients();
@ -544,7 +544,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
} }
openDelete(r: VigenciaRow) { openDelete(r: VigenciaRow) {
if (!this.isAdmin) return; if (!this.isSysAdmin) return;
this.deleteTarget = r; this.deleteTarget = r;
this.deleteOpen = true; this.deleteOpen = true;
} }
@ -613,7 +613,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
private openVigenciaLineById(lineId: string, openMode: string): void { private openVigenciaLineById(lineId: string, openMode: string): void {
this.vigenciaService.getById(lineId).subscribe({ this.vigenciaService.getById(lineId).subscribe({
next: (row) => { next: (row) => {
if (this.isAdmin && openMode !== 'details') { if (this.isSysAdmin && openMode !== 'details') {
this.openEdit(row); this.openEdit(row);
return; return;
} }
@ -643,7 +643,7 @@ export class VigenciaComponent implements OnInit, OnDestroy {
return; return;
} }
if (this.isAdmin && openMode !== 'details') { if (this.isSysAdmin && openMode !== 'details') {
this.openEdit(match); this.openEdit(match);
return; return;
} }

View File

@ -29,7 +29,7 @@ export type CreateSystemTenantUserResponse = {
}; };
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SystemAdminService { export class SysadminService {
private readonly baseApi: string; private readonly baseApi: string;
constructor(private http: HttpClient) { constructor(private http: HttpClient) {