diff --git a/src/app/components/header/header.scss b/src/app/components/header/header.scss index 5db3bb8..3ad00d1 100644 --- a/src/app/components/header/header.scss +++ b/src/app/components/header/header.scss @@ -869,13 +869,15 @@ $logo-secondary-grey: #757575; display: inline-flex; flex-direction: row; align-items: baseline; - gap: 6px; + gap: 0; line-height: 1; min-width: 0; --scale: 0.23; } .side-wordmark__line { + display: inline-flex; + align-items: baseline; font-family: 'Poppins', 'Nunito', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; font-weight: 800; font-size: calc(92px * var(--scale)); @@ -893,28 +895,29 @@ $logo-secondary-grey: #757575; background-clip: text; color: transparent; line-height: 1; + + &::after { + content: 'Gestão'; + margin-left: 0; + font: inherit; + letter-spacing: inherit; + background: linear-gradient( + 180deg, + #c8c3ff 0%, + #7a6cff 26%, + #4b3fe6 52%, + #2b21c8 74%, + #120a78 100% + ); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 1px 1px rgba(15, 23, 42, 0.12); + } } .side-wordmark__movel { - font-family: 'Poppins', 'Nunito', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; - font-weight: 800; - font-size: calc(92px * var(--scale)); - letter-spacing: -0.012em; - white-space: nowrap; - margin-left: 0; - margin-top: 0; - background: linear-gradient( - 180deg, - #aeb8c7 0%, - #6b778d 50%, - #3f4b60 100% - ); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - line-height: 1; - position: relative; - top: 0; + display: none; } .side-menu-body { padding: 16px; display: flex; flex-direction: column; gap: 4px; } .side-item { diff --git a/src/app/pages/geral/geral.ts b/src/app/pages/geral/geral.ts index 9f49a5b..a5f66c2 100644 --- a/src/app/pages/geral/geral.ts +++ b/src/app/pages/geral/geral.ts @@ -21,6 +21,7 @@ import { NavigationEnd, Router } from '@angular/router'; import { CustomSelectComponent } from '../../components/custom-select/custom-select'; import { PlanAutoFillService } from '../../services/plan-autofill.service'; import { AuthService } from '../../services/auth.service'; +import { TenantSyncService } from '../../services/tenant-sync.service'; import { firstValueFrom, Subscription, filter } from 'rxjs'; import { environment } from '../../../environments/environment'; import { confirmDeletionWithTyping } from '../../utils/destructive-confirmation'; @@ -121,7 +122,9 @@ interface ApiLineDetail { } type UpdateMobileLineRequest = Omit; -type CreateMobileLineRequest = Omit; +type CreateMobileLineRequest = Omit & { + reservaLineId?: string | null; +}; interface ClientGroupDto { cliente: string; @@ -268,7 +271,8 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { private cdr: ChangeDetectorRef, private planAutoFill: PlanAutoFillService, private authService: AuthService, - private router: Router + private router: Router, + private tenantSyncService: TenantSyncService ) {} private readonly apiBase = (() => { @@ -2716,11 +2720,12 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { private buildCreatePayload(model: any): CreateMobileLineRequest { this.calculateFinancials(model); - const { contaEmpresa: _contaEmpresa, uid: _uid, reservaLineId: _reservaLineId, ...createModelPayload } = model; + const { contaEmpresa: _contaEmpresa, uid: _uid, ...createModelPayload } = model; return { ...createModelPayload, item: Number(model.item), + reservaLineId: (model.reservaLineId ?? '').toString().trim() || null, dataBloqueio: this.dateInputToIso(model.dataBloqueio), dataEntregaOpera: this.dateInputToIso(model.dataEntregaOpera), dataEntregaCliente: this.dateInputToIso(model.dataEntregaCliente), @@ -2755,7 +2760,8 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { linha: (row.linha ?? '').toString(), chip: (row.chip ?? '').toString(), usuario: (row.usuario ?? '').toString(), - tipoDeChip: (row.tipoDeChip ?? '').toString() + tipoDeChip: (row.tipoDeChip ?? '').toString(), + reservaLineId: null }; return this.buildCreatePayload(lineModel); @@ -2772,9 +2778,13 @@ export class Geral implements OnInit, AfterViewInit, OnDestroy { private async finalizeCreateSuccess(createdCount: number) { const targetClient = (this.createModel?.cliente ?? '').toString().trim(); + const isNewClientCreated = this.createMode === 'NEW_CLIENT' && !!targetClient; this.createSaving = false; this.closeAllModals(); + if (isNewClientCreated) { + this.tenantSyncService.notifyTenantsChanged(); + } await this.showToast(this.getCreateSuccessMessage(createdCount)); diff --git a/src/app/pages/system-provision-user/system-provision-user.ts b/src/app/pages/system-provision-user/system-provision-user.ts index 95eb082..2d867ea 100644 --- a/src/app/pages/system-provision-user/system-provision-user.ts +++ b/src/app/pages/system-provision-user/system-provision-user.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; import { @@ -10,12 +10,14 @@ import { Validators, } from '@angular/forms'; import { CustomSelectComponent } from '../../components/custom-select/custom-select'; +import { Subscription } from 'rxjs'; import { SysadminService, SystemTenantDto, CreateSystemTenantUserResponse, } from '../../services/sysadmin.service'; +import { TenantSyncService } from '../../services/tenant-sync.service'; @Component({ selector: 'app-system-provision-user', @@ -24,7 +26,7 @@ import { templateUrl: './system-provision-user.html', styleUrls: ['./system-provision-user.scss'], }) -export class SystemProvisionUserPage implements OnInit { +export class SystemProvisionUserPage implements OnInit, OnDestroy { readonly sourceType = 'MobileLines.Cliente'; provisionForm: FormGroup; @@ -37,10 +39,12 @@ export class SystemProvisionUserPage implements OnInit { submitErrors: string[] = []; successMessage = ''; createdUser: CreateSystemTenantUserResponse | null = null; + private tenantChangesSub?: Subscription; constructor( private fb: FormBuilder, - private sysadminService: SysadminService + private sysadminService: SysadminService, + private tenantSyncService: TenantSyncService ) { this.provisionForm = this.fb.group( { @@ -56,6 +60,18 @@ export class SystemProvisionUserPage implements OnInit { } ngOnInit(): void { + this.tenantChangesSub = this.tenantSyncService.changes$.subscribe(() => { + this.loadTenants(); + }); + this.loadTenants(); + } + + ngOnDestroy(): void { + this.tenantChangesSub?.unsubscribe(); + } + + @HostListener('window:focus') + onWindowFocus(): void { this.loadTenants(); } diff --git a/src/app/services/tenant-sync.service.ts b/src/app/services/tenant-sync.service.ts new file mode 100644 index 0000000..dff68db --- /dev/null +++ b/src/app/services/tenant-sync.service.ts @@ -0,0 +1,38 @@ +import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { EMPTY, Observable, Subject, fromEvent, merge } from 'rxjs'; +import { filter, map } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class TenantSyncService { + private readonly storageKey = 'linegestao.tenants.updatedAt'; + private readonly localChangesSubject = new Subject(); + readonly changes$: Observable; + + private readonly isBrowser: boolean; + + constructor(@Inject(PLATFORM_ID) platformId: object) { + this.isBrowser = isPlatformBrowser(platformId); + + const storageChanges$ = this.isBrowser + ? fromEvent(window, 'storage').pipe( + filter((event) => event.key === this.storageKey && !!event.newValue), + map(() => void 0) + ) + : EMPTY; + + this.changes$ = merge(this.localChangesSubject.asObservable(), storageChanges$); + } + + notifyTenantsChanged(): void { + this.localChangesSubject.next(); + + if (!this.isBrowser) return; + + try { + localStorage.setItem(this.storageKey, String(Date.now())); + } catch { + // ignore + } + } +}