uitec-ng-header 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +11 -182
  2. package/esm2020/lib/ui-header.component.mjs +20 -26
  3. package/esm2020/lib/ui-header.module.mjs +4 -10
  4. package/esm2020/public-api.mjs +1 -17
  5. package/fesm2015/uitec-ng-header.mjs +24 -1808
  6. package/fesm2015/uitec-ng-header.mjs.map +1 -1
  7. package/fesm2020/uitec-ng-header.mjs +24 -1771
  8. package/fesm2020/uitec-ng-header.mjs.map +1 -1
  9. package/lib/ui-header.component.d.ts +8 -11
  10. package/lib/ui-header.module.d.ts +1 -2
  11. package/package.json +2 -3
  12. package/public-api.d.ts +0 -15
  13. package/esm2020/lib/assistant/components/assistant-action-card/ui-assistant-action-card.component.mjs +0 -102
  14. package/esm2020/lib/assistant/components/assistant-button/ui-assistant-button.component.mjs +0 -18
  15. package/esm2020/lib/assistant/components/assistant-composer/ui-assistant-composer.component.mjs +0 -79
  16. package/esm2020/lib/assistant/components/assistant-layout/ui-assistant-layout.component.mjs +0 -140
  17. package/esm2020/lib/assistant/components/assistant-messages/ui-assistant-messages.component.mjs +0 -144
  18. package/esm2020/lib/assistant/components/assistant-panel/ui-assistant-panel.component.mjs +0 -156
  19. package/esm2020/lib/assistant/components/assistant-text/ui-assistant-text.component.mjs +0 -26
  20. package/esm2020/lib/assistant/models/uiny-api.models.mjs +0 -12
  21. package/esm2020/lib/assistant/models/uiny.models.mjs +0 -2
  22. package/esm2020/lib/assistant/services/ui-assistant.service.mjs +0 -137
  23. package/esm2020/lib/assistant/services/uiny-api.service.mjs +0 -149
  24. package/esm2020/lib/assistant/services/uiny-chat.service.mjs +0 -368
  25. package/esm2020/lib/assistant/services/uiny-session.service.mjs +0 -74
  26. package/esm2020/lib/assistant/ui-assistant.module.mjs +0 -59
  27. package/esm2020/lib/assistant/uiny.config.mjs +0 -53
  28. package/esm2020/lib/assistant/utils/uiny-api.adapter.mjs +0 -154
  29. package/esm2020/lib/assistant/utils/uiny-contexto.mjs +0 -43
  30. package/esm2020/lib/assistant/utils/uiny-markdown.mjs +0 -122
  31. package/lib/assistant/components/assistant-action-card/ui-assistant-action-card.component.d.ts +0 -33
  32. package/lib/assistant/components/assistant-button/ui-assistant-button.component.d.ts +0 -9
  33. package/lib/assistant/components/assistant-composer/ui-assistant-composer.component.d.ts +0 -24
  34. package/lib/assistant/components/assistant-layout/ui-assistant-layout.component.d.ts +0 -51
  35. package/lib/assistant/components/assistant-messages/ui-assistant-messages.component.d.ts +0 -38
  36. package/lib/assistant/components/assistant-panel/ui-assistant-panel.component.d.ts +0 -53
  37. package/lib/assistant/components/assistant-text/ui-assistant-text.component.d.ts +0 -14
  38. package/lib/assistant/models/uiny-api.models.d.ts +0 -109
  39. package/lib/assistant/models/uiny.models.d.ts +0 -104
  40. package/lib/assistant/services/ui-assistant.service.d.ts +0 -55
  41. package/lib/assistant/services/uiny-api.service.d.ts +0 -38
  42. package/lib/assistant/services/uiny-chat.service.d.ts +0 -82
  43. package/lib/assistant/services/uiny-session.service.d.ts +0 -38
  44. package/lib/assistant/ui-assistant.module.d.ts +0 -19
  45. package/lib/assistant/uiny.config.d.ts +0 -31
  46. package/lib/assistant/utils/uiny-api.adapter.d.ts +0 -22
  47. package/lib/assistant/utils/uiny-contexto.d.ts +0 -7
  48. package/lib/assistant/utils/uiny-markdown.d.ts +0 -37
@@ -1,22 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, Optional, Inject, isDevMode, Component, ChangeDetectionStrategy, EventEmitter, Input, Output, ContentChild, ViewChild, HostListener, NgModule } from '@angular/core';
3
- import * as i1$2 from '@angular/common';
2
+ import { Injectable, EventEmitter, Component, Input, Output, Inject, ContentChild, NgModule } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
4
  import { DOCUMENT, CommonModule } from '@angular/common';
5
5
  import * as i1 from 'uitec-ng-core';
6
6
  import { UsuarioMenuDTO } from 'uitec-ng-core';
7
- import { BehaviorSubject, combineLatest, of, merge, fromEvent, defer, throwError, TimeoutError, Subject, Subscription } from 'rxjs';
8
- import { startWith, map, distinctUntilChanged, shareReplay, timeout, catchError, filter, switchMap, takeUntil, finalize } from 'rxjs/operators';
9
- import * as i1$1 from '@angular/common/http';
10
- import { HttpClient, HttpParams, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
11
- import * as i2 from 'uitec-ng-icon';
7
+ import * as i3 from 'uitec-ng-icon';
12
8
  import { UiIconModule } from 'uitec-ng-icon';
13
- import * as i5 from 'primeng/tooltip';
9
+ import * as i4$1 from 'primeng/tooltip';
14
10
  import { TooltipModule } from 'primeng/tooltip';
15
11
  import * as i4 from 'uitec-ng-user-top';
16
12
  import { UiUserTopModule } from 'uitec-ng-user-top';
17
13
  import { ReactiveFormsModule } from '@angular/forms';
18
- import * as i2$1 from '@angular/router';
19
- import { NavigationEnd } from '@angular/router';
20
14
 
21
15
  class UiHeaderService {
22
16
  constructor() { }
@@ -30,617 +24,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
30
24
  }]
31
25
  }], ctorParameters: function () { return []; } });
32
26
 
33
- const UINY_CONFIG = new InjectionToken('UINY_CONFIG');
34
- const UINY_AVATAR_PADRAO = 'assets/images/uiny/avatar.svg';
35
- const UINY_LARGURA_PADRAO = 380;
36
- const UINY_LARGURA_MIN = 340;
37
- const UINY_LARGURA_MAX = 400;
38
- const UINY_LADO_A_LADO_PADRAO = 1200;
39
- /** Mesmo limite padrão do backend (UINY_MENSAGEM_MAX_CARACTERES no uishop-ia). */
40
- const UINY_MENSAGEM_MAX_CARACTERES = 2000;
41
- /** O backend limita cada mensagem a UINY_TEMPO_MAXIMO_SEGUNDOS (90 s); damos uma folga. */
42
- const UINY_TIMEOUT_MENSAGEM_MS = 100000;
43
- const UINY_TIMEOUT_ACAO_MS = 60000;
44
- const UINY_TIMEOUT_CONSULTA_MS = 20000;
45
- /**
46
- * Resolve a URL-base: forRoot({ apiUrl }) tem prioridade; depois APP_ENVIRONMENT.domain.uiny.
47
- * Sem barra no final. Retorna null quando não configurada (o recurso fica indisponível).
48
- */
49
- function resolverUrlUiny(config, env) {
50
- var _a;
51
- const url = (config === null || config === void 0 ? void 0 : config.apiUrl) || ((_a = env === null || env === void 0 ? void 0 : env.domain) === null || _a === void 0 ? void 0 : _a.uiny) || null;
52
- if (!url || typeof url !== 'string')
53
- return null;
54
- if (!/^https?:\/\//i.test(url))
55
- return null;
56
- return url.replace(/\/+$/, '');
57
- }
58
- function larguraPainel(config) {
59
- const largura = (config === null || config === void 0 ? void 0 : config.larguraPainel) || UINY_LARGURA_PADRAO;
60
- return Math.min(UINY_LARGURA_MAX, Math.max(UINY_LARGURA_MIN, largura));
61
- }
62
- /**
63
- * Sugestões do primeiro acesso. A API não expõe o catálogo de ferramentas, então a lista
64
- * acompanha as ferramentas implementadas no uishop-ia (README, "Ferramentas implementadas").
65
- * Ao adicionar ou remover ferramentas no backend, atualize aqui.
66
- */
67
- const UINY_SUGESTOES = {
68
- studio: {
69
- textos: [
70
- 'Quais lojas e temas eu tenho?',
71
- 'Quais são as cores do meu tema?',
72
- 'Troque a cor principal do tema para #e8590c',
73
- ],
74
- // studio_alterar_cor_tema exige confirmação
75
- alteracoes: true,
76
- },
77
- erp: {
78
- textos: [
79
- 'Quais produtos estão sem estoque?',
80
- 'Como foram minhas vendas neste mês?',
81
- ],
82
- alteracoes: false,
83
- },
84
- };
85
-
86
- /** Mesma regra de ContextoTela no backend. */
87
- const TELA_VALIDA = /^[a-z0-9_\-\/.]{1,60}$/i;
88
- const CAMPOS_ID = ['idLoja', 'idLojaLeiaute', 'idTema', 'idProduto', 'idPedido'];
89
- function idPositivo(valor) {
90
- return typeof valor === 'number' && Number.isInteger(valor) && valor > 0 && valor <= 2147483647 ? valor : null;
91
- }
92
- /** Normaliza o contexto informado pelo consumidor: só campos conhecidos e valores válidos. */
93
- function normalizarContexto(contexto) {
94
- const resultado = {};
95
- if (!contexto)
96
- return resultado;
97
- const tela = typeof contexto.tela === 'string' ? contexto.tela.trim() : '';
98
- if (tela && TELA_VALIDA.test(tela))
99
- resultado.tela = tela;
100
- CAMPOS_ID.forEach(campo => {
101
- const id = idPositivo(contexto[campo]);
102
- if (id)
103
- resultado[campo] = id;
104
- });
105
- return resultado;
106
- }
107
- /** Converte para o formato de ContextoTela (campos extras geram 422 no backend). */
108
- function contextoParaApi(sistema, contexto) {
109
- const api = { sistema };
110
- if (contexto.tela)
111
- api.tela = contexto.tela;
112
- if (contexto.idLoja)
113
- api.id_loja = contexto.idLoja;
114
- if (contexto.idLojaLeiaute)
115
- api.id_loja_leiaute = contexto.idLojaLeiaute;
116
- if (contexto.idTema)
117
- api.id_tema = contexto.idTema;
118
- if (contexto.idProduto)
119
- api.id_produto = contexto.idProduto;
120
- if (contexto.idPedido)
121
- api.id_pedido = contexto.idPedido;
122
- return api;
123
- }
124
- function mesmoContexto(a, b) {
125
- var _a, _b;
126
- return ((_a = a.tela) !== null && _a !== void 0 ? _a : null) === ((_b = b.tela) !== null && _b !== void 0 ? _b : null)
127
- && CAMPOS_ID.every(campo => { var _a, _b; return ((_a = a[campo]) !== null && _a !== void 0 ? _a : null) === ((_b = b[campo]) !== null && _b !== void 0 ? _b : null); });
128
- }
129
-
130
- /**
131
- * Deriva a sessão do AuthService do uitec-ng-core (mesma fonte do JwtInterceptor).
132
- * Não lê cookies nem localStorage diretamente e não guarda o token.
133
- */
134
- class UinySessionService {
135
- constructor(authService, zone) {
136
- this.authService = authService;
137
- this.zone = zone;
138
- /** Chave recusada pela API (401). Fica sem sessão até o AuthService mudar. */
139
- this.chaveRecusada$ = new BehaviorSubject(null);
140
- this.sequencia = 0;
141
- this.ultimaChave = null;
142
- this.ultimaSessao = null;
143
- const usuario$ = this.authService.currentUser.pipe(startWith(this.authService.currentUserValue));
144
- const acesso$ = this.authService.currentAccess.pipe(startWith(this.authService.currentAccessValue));
145
- this.sessao$ = combineLatest([usuario$, acesso$, this.chaveRecusada$]).pipe(map(([usuario, acesso, recusada]) => {
146
- const chave = this.chave(usuario, acesso);
147
- if (!chave || chave === recusada)
148
- return null;
149
- return this.sessaoDaChave(chave, usuario, acesso);
150
- }), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: false }));
151
- this.online$ = typeof window === 'undefined'
152
- ? of(true)
153
- : merge(fromEvent(window, 'online').pipe(map(() => true)), fromEvent(window, 'offline').pipe(map(() => false))).pipe(startWith(typeof navigator === 'undefined' ? true : navigator.onLine !== false), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: false }));
154
- }
155
- /** Token da sessão atual, lido do AuthService no momento da requisição. */
156
- token() {
157
- const usuario = this.authService.currentUserValue;
158
- return (usuario === null || usuario === void 0 ? void 0 : usuario.token) || null;
159
- }
160
- /** A API recusou a sessão (401): esconde a Uiny até o usuário autenticar de novo. */
161
- recusarSessaoAtual() {
162
- const chave = this.chave(this.authService.currentUserValue, this.acessoAtual(this.authService.currentUserValue, this.authService.currentAccessValue));
163
- if (chave)
164
- this.zone.run(() => this.chaveRecusada$.next(chave));
165
- }
166
- acessoAtual(usuario, acesso) {
167
- var _a;
168
- if (!usuario)
169
- return null;
170
- return acesso || ((_a = usuario.acessos) === null || _a === void 0 ? void 0 : _a.find(a => a.in_atual)) || null;
171
- }
172
- chave(usuario, acesso) {
173
- var _a;
174
- const atual = this.acessoAtual(usuario, acesso);
175
- const idUsuario = (_a = usuario === null || usuario === void 0 ? void 0 : usuario.usuario) === null || _a === void 0 ? void 0 : _a.id_usuario;
176
- if (!(usuario === null || usuario === void 0 ? void 0 : usuario.token) || !idUsuario || !(atual === null || atual === void 0 ? void 0 : atual.id_empresa))
177
- return null;
178
- return `${idUsuario}|${atual.id_empresa}|${usuario.token}`;
179
- }
180
- sessaoDaChave(chave, usuario, acesso) {
181
- if (chave === this.ultimaChave && this.ultimaSessao)
182
- return this.ultimaSessao;
183
- const atual = this.acessoAtual(usuario, acesso);
184
- this.ultimaChave = chave;
185
- this.ultimaSessao = {
186
- id: ++this.sequencia,
187
- idUsuario: Number(usuario.usuario.id_usuario),
188
- idEmpresa: Number(atual.id_empresa),
189
- };
190
- return this.ultimaSessao;
191
- }
192
- }
193
- UinySessionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinySessionService, deps: [{ token: i1.AuthService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
194
- UinySessionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinySessionService, providedIn: 'root' });
195
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinySessionService, decorators: [{
196
- type: Injectable,
197
- args: [{ providedIn: 'root' }]
198
- }], ctorParameters: function () { return [{ type: i1.AuthService }, { type: i0.NgZone }]; } });
199
-
200
- /** Situação da API (Constants::ACAO_*) -> estado da tela. */
201
- const ESTADOS = {
202
- pendente: 'pendente',
203
- executando: 'executando',
204
- executada: 'concluida',
205
- cancelada: 'cancelada',
206
- expirada: 'expirada',
207
- invalidada: 'invalidada',
208
- falhou: 'falha',
209
- indeterminada: 'indeterminada',
210
- };
211
- const UUID = /^[0-9a-f-]{36}$/i;
212
- const MAX_DETALHES = 12;
213
- const MAX_VALOR = 200;
214
- /** Datas do backend: "Y-m-d H:i:s" (America/Sao_Paulo) ou ISO. */
215
- function lerData(valor) {
216
- if (typeof valor !== 'string' || !valor)
217
- return null;
218
- const iso = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/.test(valor) ? valor.replace(' ', 'T') + '-03:00' : valor;
219
- const data = new Date(iso);
220
- return isNaN(data.getTime()) ? null : data;
221
- }
222
- function idValido(valor) {
223
- return typeof valor === 'string' && UUID.test(valor) ? valor : null;
224
- }
225
- function inteiro(valor) {
226
- const n = typeof valor === 'string' && valor.trim() !== '' ? Number(valor) : valor;
227
- return typeof n === 'number' && Number.isInteger(n) && n > 0 ? n : null;
228
- }
229
- function texto(valor, max = 20000) {
230
- if (typeof valor !== 'string')
231
- return '';
232
- return valor.length > max ? valor.slice(0, max) : valor;
233
- }
234
- function adaptarConversa(api) {
235
- const id = idValido(api === null || api === void 0 ? void 0 : api.id);
236
- if (!id)
237
- return null;
238
- return {
239
- id,
240
- titulo: texto(api.titulo, 120) || null,
241
- sistemaOrigem: api.sistema_origem,
242
- idLoja: inteiro(api.id_loja),
243
- atualizadaEm: lerData(api.dt_alteracao),
244
- };
245
- }
246
- function adaptarMensagem(api) {
247
- var _a;
248
- const id = idValido(api === null || api === void 0 ? void 0 : api.id);
249
- if (!id || (api.papel !== 'usuario' && api.papel !== 'assistente'))
250
- return null;
251
- return {
252
- id,
253
- papel: api.papel,
254
- estado: typeof api.estado === 'string' ? api.estado : null,
255
- texto: texto(api.texto),
256
- idAcao: idValido((_a = api.acao_pendente) === null || _a === void 0 ? void 0 : _a.id),
257
- opcoes: adaptarOpcoes(api.apresentacao),
258
- criadaEm: lerData(api.dt_criacao),
259
- };
260
- }
261
- /** Bloco "opcoes" do esclarecimento: só strings curtas. */
262
- function adaptarOpcoes(apresentacao) {
263
- var _a;
264
- if (!Array.isArray(apresentacao))
265
- return [];
266
- const bloco = apresentacao.find(b => b && typeof b === 'object' && b.tipo === 'opcoes');
267
- const opcoes = (_a = bloco === null || bloco === void 0 ? void 0 : bloco.dados) === null || _a === void 0 ? void 0 : _a.opcoes;
268
- if (!Array.isArray(opcoes))
269
- return [];
270
- return opcoes.filter(o => typeof o === 'string' && o.trim()).map(o => o.trim().slice(0, 80)).slice(0, 6);
271
- }
272
- function adaptarAcao(api) {
273
- var _a;
274
- const id = idValido(api === null || api === void 0 ? void 0 : api.id);
275
- if (!id)
276
- return null;
277
- return {
278
- id,
279
- ferramenta: texto(api.ferramenta, 80),
280
- resumo: texto(api.resumo, 2000),
281
- detalhes: adaptarDetalhes(api.detalhes),
282
- // situação desconhecida: não afirmamos sucesso nem falha
283
- estado: (_a = ESTADOS[api.situacao]) !== null && _a !== void 0 ? _a : 'indeterminada',
284
- expiraEm: lerData(api.dt_expiracao),
285
- };
286
- }
287
- /**
288
- * "detalhes" vem da PropostaAlteracao da ferramenta. Só aproveitamos pares rótulo/valor
289
- * com valores primitivos; nada é interpretado como HTML. Identificadores internos
290
- * (tipo, id_*) não são exibidos.
291
- */
292
- function adaptarDetalhes(detalhes) {
293
- var _a;
294
- const itens = [];
295
- const adicionar = (rotulo, valor) => {
296
- if (itens.length >= MAX_DETALHES)
297
- return;
298
- if (typeof rotulo !== 'string' || !rotulo.trim())
299
- return;
300
- if (typeof valor !== 'string' && typeof valor !== 'number' && typeof valor !== 'boolean')
301
- return;
302
- const v = typeof valor === 'boolean' ? (valor ? 'Sim' : 'Não') : String(valor);
303
- if (!v.trim())
304
- return;
305
- itens.push({ rotulo: humanizar(rotulo).slice(0, 80), valor: v.slice(0, MAX_VALOR) });
306
- };
307
- if (Array.isArray(detalhes)) {
308
- detalhes.forEach(item => {
309
- var _a;
310
- if (item && typeof item === 'object')
311
- adicionar((_a = item.rotulo) !== null && _a !== void 0 ? _a : item.campo, item.valor);
312
- });
313
- return itens;
314
- }
315
- if (!detalhes || typeof detalhes !== 'object')
316
- return itens;
317
- const d = detalhes;
318
- if (d.tipo === 'alteracao_cor') {
319
- // tools/studio/AlterarCorTemaStudio.php
320
- adicionar('Tema', d.tema);
321
- adicionar('Loja', d.loja);
322
- adicionar('Cor', d.rotulo);
323
- adicionar('Valor atual', (_a = d.valor_atual) !== null && _a !== void 0 ? _a : 'sem valor definido');
324
- adicionar('Novo valor', d.valor_novo);
325
- return itens;
326
- }
327
- Object.keys(d)
328
- .filter(chave => chave !== 'tipo' && !/^id(_|$)/.test(chave))
329
- .forEach(chave => adicionar(chave, d[chave]));
330
- return itens;
331
- }
332
- function humanizar(chave) {
333
- if (/[A-Z ]/.test(chave))
334
- return chave;
335
- const t = chave.replace(/[_-]+/g, ' ').trim();
336
- return t.charAt(0).toUpperCase() + t.slice(1);
337
- }
338
- /**
339
- * Converte instrucoes_interface (Constants::INSTRUCOES_INTERFACE) em eventos tipados.
340
- * Tipos fora da lista e campos inesperados são descartados. Nada é executado nem navegado aqui.
341
- */
342
- function adaptarInstrucoes(instrucoes) {
343
- if (!Array.isArray(instrucoes))
344
- return [];
345
- const eventos = [];
346
- for (const item of instrucoes.slice(0, 10)) {
347
- if (!item || typeof item !== 'object')
348
- continue;
349
- const bruto = item;
350
- if (bruto.tipo === 'atualizar_preview_tema') {
351
- const idLojaLeiaute = inteiro(bruto.id_loja_leiaute);
352
- if (idLojaLeiaute)
353
- eventos.push({ tipo: 'tema_atualizado', idLojaLeiaute });
354
- }
355
- }
356
- return eventos;
357
- }
358
-
359
- const MENSAGENS_PADRAO = {
360
- sessao: 'Sua sessão expirou. Entre novamente na Uishop para continuar com a Uiny.',
361
- rede: 'Não foi possível conectar à Uiny. Verifique sua internet e tente de novo.',
362
- timeout: 'A Uiny demorou demais para responder. Tente de novo em instantes.',
363
- conflito: 'Não foi possível concluir agora. Atualize e tente de novo.',
364
- nao_encontrado: 'Não encontramos o que você pediu. Ele pode ter sido removido.',
365
- invalido: 'Não foi possível processar esse pedido.',
366
- limite: 'Você enviou muitas mensagens em pouco tempo. Aguarde um pouco e tente de novo.',
367
- servidor: 'A Uiny está com instabilidade no momento. Tente de novo em instantes.',
368
- configuracao: 'A Uiny não está configurada neste sistema.',
369
- };
370
- /**
371
- * Cliente HTTP da API da Uiny (uishop-ia, /api/v1).
372
- *
373
- * Usa um HttpClient sobre o HttpBackend, sem os interceptors da aplicação:
374
- * - o ErrorInterceptor do uitec-ng-core troca o erro por uma string (perde status e o corpo,
375
- * necessários para sessão expirada, ação expirada/invalidada e conflitos) e abre um toast global;
376
- * - o SpinnerInterceptor cobriria a tela a cada mensagem.
377
- * O token é o mesmo usado pelo JwtInterceptor (AuthService.currentUserValue.token) e só é
378
- * anexado a requisições cuja URL começa com a URL-base configurada da Uiny.
379
- */
380
- class UinyApiService {
381
- constructor(httpBackend, session, config, env) {
382
- this.session = session;
383
- this.http = new HttpClient(httpBackend);
384
- this.baseUrl = resolverUrlUiny(config, env);
385
- }
386
- get configurada() {
387
- return !!this.baseUrl;
388
- }
389
- listarConversas() {
390
- return defer(() => this.request('GET', '/conversas', null, UINY_TIMEOUT_CONSULTA_MS, new HttpParams().set('page', 1).set('per_page', 20)));
391
- }
392
- criarConversa(contexto) {
393
- return defer(() => this.request('POST', '/conversas', { contexto }, UINY_TIMEOUT_CONSULTA_MS));
394
- }
395
- /** Página mais recente do histórico (até 100 mensagens), em ordem cronológica. */
396
- historico(idConversa) {
397
- return defer(() => this.request('GET', `/conversas/${this.id(idConversa)}`, null, UINY_TIMEOUT_CONSULTA_MS, new HttpParams().set('page', 1).set('per_page', 100)));
398
- }
399
- enviarMensagem(idConversa, texto, contexto) {
400
- return defer(() => this.request('POST', `/conversas/${this.id(idConversa)}/mensagens`, { texto, contexto }, UINY_TIMEOUT_MENSAGEM_MS));
401
- }
402
- /** Confirma pelo id da ação persistida. Nenhum argumento é enviado. Nunca é repetida automaticamente. */
403
- confirmarAcao(idAcao) {
404
- return defer(() => this.request('POST', `/acoes/${this.id(idAcao)}/confirmar`, {}, UINY_TIMEOUT_ACAO_MS));
405
- }
406
- cancelarAcao(idAcao) {
407
- return defer(() => this.request('POST', `/acoes/${this.id(idAcao)}/cancelar`, {}, UINY_TIMEOUT_CONSULTA_MS));
408
- }
409
- id(valor) {
410
- const id = idValido(valor);
411
- if (!id)
412
- throw this.erro('invalido', 0);
413
- return id;
414
- }
415
- request(method, path, body, timeoutMs, params) {
416
- if (!this.baseUrl) {
417
- return throwError(() => this.erro('configuracao', 0));
418
- }
419
- const url = this.baseUrl + path;
420
- const token = this.session.token();
421
- if (!token) {
422
- return throwError(() => this.erro('sessao', 401));
423
- }
424
- let headers = new HttpHeaders({ 'Accept': 'application/json' });
425
- // Garantia explícita: o token só segue para a URL-base da Uiny.
426
- if (url.startsWith(this.baseUrl + '/')) {
427
- headers = headers.set('Authorization', `Bearer ${token}`);
428
- }
429
- if (method === 'POST') {
430
- headers = headers.set('Content-Type', 'application/json');
431
- }
432
- return this.http.request(method, url, { body: body !== null && body !== void 0 ? body : undefined, headers, params, responseType: 'json' }).pipe(timeout(timeoutMs), catchError(err => throwError(() => this.normalizarErro(err))));
433
- }
434
- normalizarErro(err) {
435
- var _a, _b, _c;
436
- if (err instanceof TimeoutError) {
437
- return this.erro('timeout', 0);
438
- }
439
- if (!(err instanceof HttpErrorResponse)) {
440
- return this.erro('servidor', 0);
441
- }
442
- const corpo = err.error && typeof err.error === 'object' ? err.error : null;
443
- const correlacao = this.textoSeguro(corpo === null || corpo === void 0 ? void 0 : corpo.id_correlacao, 64) || this.textoSeguro((_a = err.headers) === null || _a === void 0 ? void 0 : _a.get('X-Correlation-Id'), 64);
444
- const codigo = this.textoSeguro((_b = corpo === null || corpo === void 0 ? void 0 : corpo.erro) === null || _b === void 0 ? void 0 : _b.codigo, 60);
445
- const mensagemApi = this.textoSeguro((_c = corpo === null || corpo === void 0 ? void 0 : corpo.erro) === null || _c === void 0 ? void 0 : _c.mensagem, 300);
446
- let tipo;
447
- if (err.status === 0)
448
- tipo = 'rede';
449
- else if (err.status === 401)
450
- tipo = 'sessao';
451
- else if (err.status === 404)
452
- tipo = 'nao_encontrado';
453
- else if (err.status === 409)
454
- tipo = 'conflito';
455
- else if (err.status === 429)
456
- tipo = 'limite';
457
- else if (err.status === 400 || err.status === 403 || err.status === 422)
458
- tipo = 'invalido';
459
- else
460
- tipo = 'servidor';
461
- if (tipo === 'sessao') {
462
- this.session.recusarSessaoAtual();
463
- }
464
- // 502/503/504 trazem mensagens próprias e seguras (INTEGRACAO_*, PROVEDOR_IA_INDISPONIVEL).
465
- // 500 (INTERNO) e falhas de rede usam sempre o texto padrão.
466
- const usarMensagemApi = tipo !== 'rede' && tipo !== 'sessao' && err.status !== 500;
467
- const mensagem = (usarMensagemApi && mensagemApi) || MENSAGENS_PADRAO[tipo];
468
- return { tipo, status: err.status, codigo, mensagem, correlacao, corpo };
469
- }
470
- erro(tipo, status) {
471
- return { tipo, status, codigo: null, mensagem: MENSAGENS_PADRAO[tipo], correlacao: null };
472
- }
473
- textoSeguro(valor, max) {
474
- if (typeof valor !== 'string')
475
- return null;
476
- const texto = valor.trim();
477
- if (!texto || /bearer\s/i.test(texto))
478
- return null;
479
- return texto.length > max ? texto.slice(0, max) + '…' : texto;
480
- }
481
- }
482
- UinyApiService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyApiService, deps: [{ token: i1$1.HttpBackend }, { token: UinySessionService }, { token: UINY_CONFIG, optional: true }, { token: 'APP_ENVIRONMENT', optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
483
- UinyApiService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyApiService, providedIn: 'root' });
484
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyApiService, decorators: [{
485
- type: Injectable,
486
- args: [{ providedIn: 'root' }]
487
- }], ctorParameters: function () {
488
- return [{ type: i1$1.HttpBackend }, { type: UinySessionService }, { type: undefined, decorators: [{
489
- type: Optional
490
- }, {
491
- type: Inject,
492
- args: [UINY_CONFIG]
493
- }] }, { type: undefined, decorators: [{
494
- type: Optional
495
- }, {
496
- type: Inject,
497
- args: ['APP_ENVIRONMENT']
498
- }] }];
499
- } });
500
-
501
- /**
502
- * Estado compartilhado da Uiny: quem habilitou (o header), quem hospeda o painel
503
- * (o ui-assistant-layout), se está aberto, o contexto da tela e os eventos de saída.
504
- *
505
- * O painel só fica disponível quando TODAS as condições valem:
506
- * - algum ui-header está com [showAssistant]="true";
507
- * - existe um ui-assistant-layout na página para receber o painel;
508
- * - há sessão autenticada no AuthService (e a API não a recusou);
509
- * - a URL da API está configurada.
510
- */
511
- class UiAssistantService {
512
- constructor(session, api, config) {
513
- this.session = session;
514
- this.api = api;
515
- this.config = config;
516
- this.habilitadores$ = new BehaviorSubject(new Set());
517
- this.hosts$ = new BehaviorSubject(0);
518
- this.abertoInterno$ = new BehaviorSubject(false);
519
- this.contextoInterno$ = new BehaviorSubject({});
520
- this.eventosInternos$ = new Subject();
521
- this.gatilho = null;
522
- this.avisouSemHost = false;
523
- this.contexto$ = this.contextoInterno$.pipe(distinctUntilChanged(mesmoContexto));
524
- /** Ações concluídas e instruções de interface vindas da API, já validadas. */
525
- this.eventos$ = this.eventosInternos$.asObservable();
526
- this.disponivel$ = combineLatest([this.habilitadores$, this.hosts$, this.session.sessao$]).pipe(map(([habilitadores, hosts, sessao]) => habilitadores.size > 0 && hosts > 0 && !!sessao && this.api.configurada && !!this.config), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: false }));
527
- this.aberto$ = combineLatest([this.abertoInterno$, this.disponivel$]).pipe(map(([aberto, disponivel]) => aberto && disponivel), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: false }));
528
- // Indisponível (logout, sessão recusada, header sem a flag): fecha sem devolver foco.
529
- this.disponivel$.subscribe(disponivel => {
530
- if (!disponivel && this.abertoInterno$.value) {
531
- this.gatilho = null;
532
- this.abertoInterno$.next(false);
533
- }
534
- });
535
- combineLatest([this.habilitadores$, this.hosts$]).subscribe(([habilitadores, hosts]) => this.diagnosticar(habilitadores.size > 0, hosts));
536
- }
537
- get aberto() {
538
- return this.abertoInterno$.value;
539
- }
540
- get sistemaOrigem() {
541
- var _a, _b;
542
- return (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.sistemaOrigem) !== null && _b !== void 0 ? _b : null;
543
- }
544
- abrir(gatilho) {
545
- if (gatilho)
546
- this.gatilho = gatilho;
547
- this.abertoInterno$.next(true);
548
- }
549
- /** Fecha o painel e devolve o foco ao botão que o abriu, se ele ainda existir. */
550
- fechar(devolverFoco = true) {
551
- if (!this.abertoInterno$.value)
552
- return;
553
- this.abertoInterno$.next(false);
554
- const gatilho = this.gatilho;
555
- this.gatilho = null;
556
- if (devolverFoco && gatilho) {
557
- // espera o layout reexibir a aplicação (no celular ela fica recolhida)
558
- setTimeout(() => gatilho.isConnected && gatilho.focus());
559
- }
560
- }
561
- alternar(gatilho) {
562
- this.abertoInterno$.value ? this.fechar() : this.abrir(gatilho);
563
- }
564
- /** Substitui o contexto da tela atual. Campos fora do tipo UinyContexto são ignorados. */
565
- definirContexto(contexto) {
566
- this.contextoInterno$.next(normalizarContexto(contexto));
567
- }
568
- /** Altera apenas os campos informados do contexto. */
569
- atualizarContexto(parcial) {
570
- this.contextoInterno$.next(normalizarContexto(Object.assign(Object.assign({}, this.contextoInterno$.value), parcial)));
571
- }
572
- get contexto() {
573
- return this.contextoInterno$.value;
574
- }
575
- /** @internal usado pelo ui-header */
576
- definirHabilitado(origem, habilitado) {
577
- const atual = new Set(this.habilitadores$.value);
578
- habilitado ? atual.add(origem) : atual.delete(origem);
579
- if (atual.size !== this.habilitadores$.value.size)
580
- this.habilitadores$.next(atual);
581
- }
582
- /** @internal usado pelo ui-assistant-layout */
583
- registrarHost() {
584
- this.hosts$.next(this.hosts$.value + 1);
585
- let removido = false;
586
- return () => {
587
- if (removido)
588
- return;
589
- removido = true;
590
- this.hosts$.next(Math.max(0, this.hosts$.value - 1));
591
- };
592
- }
593
- /** @internal usado pelo serviço de conversa */
594
- emitir(evento) {
595
- this.eventosInternos$.next(evento);
596
- }
597
- diagnosticar(habilitado, hosts) {
598
- if (!isDevMode() || !habilitado)
599
- return;
600
- if (!this.config) {
601
- console.error('[Uiny] showAssistant está ativo, mas UiAssistantModule.forRoot({ sistemaOrigem }) não foi importado.');
602
- }
603
- else if (!this.api.configurada) {
604
- console.error('[Uiny] URL da API não configurada. Informe APP_ENVIRONMENT.domain.uiny ou forRoot({ apiUrl }).');
605
- }
606
- else if (hosts === 0 && !this.avisouSemHost) {
607
- // o layout pode ser criado logo depois do header; avisa só se continuar faltando
608
- this.avisouSemHost = true;
609
- setTimeout(() => {
610
- if (this.hosts$.value === 0) {
611
- console.warn('[Uiny] showAssistant está ativo, mas nenhum <ui-assistant-layout> envolve a aplicação. O botão fica oculto.');
612
- }
613
- }, 2000);
614
- }
615
- }
616
- }
617
- UiAssistantService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantService, deps: [{ token: UinySessionService }, { token: UinyApiService }, { token: UINY_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
618
- UiAssistantService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantService, providedIn: 'root' });
619
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantService, decorators: [{
620
- type: Injectable,
621
- args: [{ providedIn: 'root' }]
622
- }], ctorParameters: function () {
623
- return [{ type: UinySessionService }, { type: UinyApiService }, { type: undefined, decorators: [{
624
- type: Optional
625
- }, {
626
- type: Inject,
627
- args: [UINY_CONFIG]
628
- }] }];
629
- } });
630
-
631
- /** Botão que abre/fecha a Uiny. Só aparece quando a assistente está disponível. */
632
- class UiAssistantButtonComponent {
633
- constructor(assistant) {
634
- this.assistant = assistant;
635
- }
636
- }
637
- UiAssistantButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantButtonComponent, deps: [{ token: UiAssistantService }], target: i0.ɵɵFactoryTarget.Component });
638
- UiAssistantButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantButtonComponent, selector: "ui-assistant-button", ngImport: i0, template: "<button *ngIf=\"assistant.disponivel$ | async\" #botao type=\"button\" class=\"uiny-trigger\"\n [class.is-open]=\"assistant.aberto$ | async\"\n [attr.aria-expanded]=\"(assistant.aberto$ | async) ? 'true' : 'false'\"\n aria-controls=\"uiny-painel\"\n [attr.aria-label]=\"(assistant.aberto$ | async) ? 'Fechar a Uiny, assistente da Uishop' : 'Abrir a Uiny, assistente da Uishop'\"\n [title]=\"(assistant.aberto$ | async) ? 'Fechar a Uiny' : 'Falar com a Uiny'\"\n (click)=\"assistant.alternar(botao)\">\n <svg class=\"uiny-trigger__mark\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M12 2.5c.5 3.9 2.6 6.9 6.4 8.2.5.2.5.9 0 1.1-3.8 1.3-5.9 4.3-6.4 8.2-.1.6-.9.6-1 0-.5-3.9-2.6-6.9-6.4-8.2-.5-.2-.5-.9 0-1.1 3.8-1.3 5.9-4.3 6.4-8.2.1-.6.9-.6 1 0Z\" fill=\"currentColor\"/>\n <path d=\"M19 2.8c.2 1.3.9 2.2 2.1 2.6.2.1.2.3 0 .4-1.2.4-1.9 1.3-2.1 2.6 0 .2-.3.2-.3 0-.2-1.3-.9-2.2-2.1-2.6-.2-.1-.2-.3 0-.4 1.2-.4 1.9-1.3 2.1-2.6 0-.2.3-.2.3 0Z\" fill=\"currentColor\" opacity=\".7\"/>\n </svg>\n <span class=\"uiny-trigger__label\">Uiny</span>\n</button>\n", styles: [":host{display:inline-flex}.uiny-trigger{display:inline-flex;align-items:center;gap:6px;height:34px;padding:0 14px 0 10px;border:1px solid rgba(115,102,255,.28);border-radius:999px;background:rgba(115,102,255,.08);color:var(--theme-default, #7366ff);font-size:13px;font-weight:600;line-height:1;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.uiny-trigger:hover{background:rgba(115,102,255,.14);border-color:#7366ff73}.uiny-trigger:focus-visible{outline:2px solid var(--theme-default, #7366ff);outline-offset:2px}.uiny-trigger.is-open{background:var(--theme-default, #7366ff);border-color:var(--theme-default, #7366ff);color:#fff}.uiny-trigger__mark{flex:0 0 auto}@media (max-width: 575.98px){.uiny-trigger{width:34px;padding:0;justify-content:center}.uiny-trigger__label{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}@media (prefers-reduced-motion: reduce){.uiny-trigger{transition:none}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
639
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantButtonComponent, decorators: [{
640
- type: Component,
641
- args: [{ selector: 'ui-assistant-button', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button *ngIf=\"assistant.disponivel$ | async\" #botao type=\"button\" class=\"uiny-trigger\"\n [class.is-open]=\"assistant.aberto$ | async\"\n [attr.aria-expanded]=\"(assistant.aberto$ | async) ? 'true' : 'false'\"\n aria-controls=\"uiny-painel\"\n [attr.aria-label]=\"(assistant.aberto$ | async) ? 'Fechar a Uiny, assistente da Uishop' : 'Abrir a Uiny, assistente da Uishop'\"\n [title]=\"(assistant.aberto$ | async) ? 'Fechar a Uiny' : 'Falar com a Uiny'\"\n (click)=\"assistant.alternar(botao)\">\n <svg class=\"uiny-trigger__mark\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M12 2.5c.5 3.9 2.6 6.9 6.4 8.2.5.2.5.9 0 1.1-3.8 1.3-5.9 4.3-6.4 8.2-.1.6-.9.6-1 0-.5-3.9-2.6-6.9-6.4-8.2-.5-.2-.5-.9 0-1.1 3.8-1.3 5.9-4.3 6.4-8.2.1-.6.9-.6 1 0Z\" fill=\"currentColor\"/>\n <path d=\"M19 2.8c.2 1.3.9 2.2 2.1 2.6.2.1.2.3 0 .4-1.2.4-1.9 1.3-2.1 2.6 0 .2-.3.2-.3 0-.2-1.3-.9-2.2-2.1-2.6-.2-.1-.2-.3 0-.4 1.2-.4 1.9-1.3 2.1-2.6 0-.2.3-.2.3 0Z\" fill=\"currentColor\" opacity=\".7\"/>\n </svg>\n <span class=\"uiny-trigger__label\">Uiny</span>\n</button>\n", styles: [":host{display:inline-flex}.uiny-trigger{display:inline-flex;align-items:center;gap:6px;height:34px;padding:0 14px 0 10px;border:1px solid rgba(115,102,255,.28);border-radius:999px;background:rgba(115,102,255,.08);color:var(--theme-default, #7366ff);font-size:13px;font-weight:600;line-height:1;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.uiny-trigger:hover{background:rgba(115,102,255,.14);border-color:#7366ff73}.uiny-trigger:focus-visible{outline:2px solid var(--theme-default, #7366ff);outline-offset:2px}.uiny-trigger.is-open{background:var(--theme-default, #7366ff);border-color:var(--theme-default, #7366ff);color:#fff}.uiny-trigger__mark{flex:0 0 auto}@media (max-width: 575.98px){.uiny-trigger{width:34px;padding:0;justify-content:center}.uiny-trigger__label{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}}@media (prefers-reduced-motion: reduce){.uiny-trigger{transition:none}}\n"] }]
642
- }], ctorParameters: function () { return [{ type: UiAssistantService }]; } });
643
-
644
27
  class MyAccountComponent {
645
28
  constructor(authService, urlService) {
646
29
  this.authService = authService;
@@ -665,7 +48,7 @@ class MyAccountComponent {
665
48
  }
666
49
  }
667
50
  MyAccountComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MyAccountComponent, deps: [{ token: i1.AuthService }, { token: i1.BaseUrlService }], target: i0.ɵɵFactoryTarget.Component });
668
- MyAccountComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: MyAccountComponent, selector: "my-account", inputs: { menus: "menus" }, outputs: { onNavigate: "onNavigate" }, ngImport: i0, template: "<ui-user-top [urlImage]=\"urlService.imagemUsuario({model: userObs?.usuario})\" [usuario]=\"userObs?.usuario\"\r\n [acesso]=\"accessObs\"></ui-user-top>\r\n\r\n<ul class=\"profile-dropdown onhover-show-div\">\r\n <li *ngFor=\"let col of menus\">\r\n <a (click)=\"clickMenu(col)\">\r\n <ui-icon [icon]=\"col.ds_icone\" size=\"20\"></ui-icon>\r\n <span>{{col.no_funcionalidade}}</span>\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"javascript:void(0)\" (click)=\"logout()\" class=\"text-danger\">\r\n <ui-icon icon=\"login\" size=\"20\"></ui-icon>\r\n <span>Sair</span>\r\n </a>\r\n </li>\r\n</ul>\r\n", styles: ["a{display:flex!important;align-items:center!important}ui-icon{display:flex;padding-right:1rem}ui-image{margin-top:4px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "component", type: i4.UiUserTopComponent, selector: "ui-user-top", inputs: ["urlImage", "usuario", "acesso"] }] });
51
+ MyAccountComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: MyAccountComponent, selector: "my-account", inputs: { menus: "menus" }, outputs: { onNavigate: "onNavigate" }, ngImport: i0, template: "<ui-user-top [urlImage]=\"urlService.imagemUsuario({model: userObs?.usuario})\" [usuario]=\"userObs?.usuario\"\r\n [acesso]=\"accessObs\"></ui-user-top>\r\n\r\n<ul class=\"profile-dropdown onhover-show-div\">\r\n <li *ngFor=\"let col of menus\">\r\n <a (click)=\"clickMenu(col)\">\r\n <ui-icon [icon]=\"col.ds_icone\" size=\"20\"></ui-icon>\r\n <span>{{col.no_funcionalidade}}</span>\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"javascript:void(0)\" (click)=\"logout()\" class=\"text-danger\">\r\n <ui-icon icon=\"login\" size=\"20\"></ui-icon>\r\n <span>Sair</span>\r\n </a>\r\n </li>\r\n</ul>\r\n", styles: ["a{display:flex!important;align-items:center!important}ui-icon{display:flex;padding-right:1rem}ui-image{margin-top:4px}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i3.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "component", type: i4.UiUserTopComponent, selector: "ui-user-top", inputs: ["urlImage", "usuario", "acesso"] }] });
669
52
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MyAccountComponent, decorators: [{
670
53
  type: Component,
671
54
  args: [{ selector: 'my-account', template: "<ui-user-top [urlImage]=\"urlService.imagemUsuario({model: userObs?.usuario})\" [usuario]=\"userObs?.usuario\"\r\n [acesso]=\"accessObs\"></ui-user-top>\r\n\r\n<ul class=\"profile-dropdown onhover-show-div\">\r\n <li *ngFor=\"let col of menus\">\r\n <a (click)=\"clickMenu(col)\">\r\n <ui-icon [icon]=\"col.ds_icone\" size=\"20\"></ui-icon>\r\n <span>{{col.no_funcionalidade}}</span>\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"javascript:void(0)\" (click)=\"logout()\" class=\"text-danger\">\r\n <ui-icon icon=\"login\" size=\"20\"></ui-icon>\r\n <span>Sair</span>\r\n </a>\r\n </li>\r\n</ul>\r\n", styles: ["a{display:flex!important;align-items:center!important}ui-icon{display:flex;padding-right:1rem}ui-image{margin-top:4px}\n"] }]
@@ -676,30 +59,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
676
59
  }] } });
677
60
 
678
61
  class UiHeaderComponent {
679
- constructor(authService, urlService, layoutService, iframeService, assistantService, document) {
62
+ constructor(authService, urlService, layoutService, iframeService, document) {
680
63
  this.authService = authService;
681
64
  this.urlService = urlService;
682
65
  this.layoutService = layoutService;
683
66
  this.iframeService = iframeService;
684
- this.assistantService = assistantService;
685
67
  this.document = document;
686
68
  this.sidebar = true;
69
+ this.fullWidth = false;
687
70
  this.redirectHome = true;
688
71
  this.darkDefault = "light";
689
- /** Exibe o botão da Uiny (assistente virtual). Exige um <ui-assistant-layout> envolvendo a aplicação. */
690
- this.showAssistant = false;
691
72
  this.collapseSidebarChange = new EventEmitter();
692
73
  this.fullScreenChange = new EventEmitter();
693
74
  this.headerBackdropChange = new EventEmitter();
694
75
  this.onNavigate = new EventEmitter();
695
- }
696
- ngOnChanges(changes) {
697
- if (changes['showAssistant']) {
698
- this.assistantService.definirHabilitado(this, !!this.showAssistant);
699
- }
700
- }
701
- ngOnDestroy() {
702
- this.assistantService.definirHabilitado(this, false);
76
+ this.onUpgrade = new EventEmitter();
703
77
  }
704
78
  ngOnInit() {
705
79
  this.elem = document.documentElement;
@@ -765,6 +139,10 @@ class UiHeaderComponent {
765
139
  this.statuSidebar = this.collapseSidebar;
766
140
  localStorage.setItem('local', this.statuSidebar);
767
141
  }
142
+ fecharBackdrop() {
143
+ this.headerBackdrop = null;
144
+ this.headerBackdropChange.emit(false);
145
+ }
768
146
  sidebarToggle() {
769
147
  this.collapseSidebar = !this.collapseSidebar;
770
148
  this.setStatusSideBar();
@@ -839,13 +217,13 @@ class UiHeaderComponent {
839
217
  }
840
218
  }
841
219
  }
842
- UiHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderComponent, deps: [{ token: i1.AuthService }, { token: i1.BaseUrlService }, { token: i1.LayoutService }, { token: i1.IframeService }, { token: UiAssistantService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
843
- UiHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiHeaderComponent, selector: "ui-header", inputs: { collapseSidebar: "collapseSidebar", fullScreen: "fullScreen", headerBackdrop: "headerBackdrop", isFree: "isFree", menus: "menus", sidebar: "sidebar", redirectHome: "redirectHome", darkDefault: "darkDefault", showAssistant: "showAssistant" }, outputs: { collapseSidebarChange: "collapseSidebarChange", fullScreenChange: "fullScreenChange", headerBackdropChange: "headerBackdropChange", onNavigate: "onNavigate" }, queries: [{ propertyName: "left", first: true, predicate: ["left"], descendants: true }, { propertyName: "center", first: true, predicate: ["center"], descendants: true }, { propertyName: "right", first: true, predicate: ["right"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Page Header Backdrop -->\r\n<div *ngIf=\"headerBackdrop\" (click)=\"navService.hideAllPopUps()\" class=\"header-backgrop\"></div>\r\n\r\n<!-- Page Header Start -->\r\n<div class=\"page-header\" [class.close_icon]=\"collapseSidebar\" [class.not-sidebar]=\"!sidebar\">\r\n <div class=\"header-wrapper d-flex m-0 justify-content-between\">\r\n <div class=\"header-logo-wrapper col-auto d-lg-none px-0\">\r\n <div *ngIf=\"sidebar\" class=\"toggle-sidebar\" (click)=\"sidebarToggle()\">\r\n <ui-icon icon=\"menu\" class=\"status_toggle middle\"></ui-icon>\r\n </div>\r\n <a (click)=\"home()\" [class.cursor-pointer]=\"redirectHome\">\r\n <img class=\"img-fluid for-dark image-logo d-md-none\" src=\"assets/images/logo/logo_dark.png\" alt=\"\" />\r\n </a>\r\n </div>\r\n\r\n <div class=\"left-header horizontal-wrapper ps-0\">\r\n <ng-container [ngTemplateOutlet]=\"left\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-center\">\r\n <ng-container [ngTemplateOutlet]=\"center\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-right p-0\">\r\n <ul class=\"nav-menus\">\r\n <!-- <li class=\"language-nav\" (click)=\"languageToggle()\">\r\n <header-languages></header-languages>\r\n </li> -->\r\n <ng-container *ngIf=\"isFree; then liUpgrade; else liEmpty\"></ng-container>\r\n\r\n <ng-template #liUpgrade>\r\n <li>\r\n <button\r\n class=\"d-flex align-items-center justify-content-center border-0 rounded-pill gap-2 b-color\"\r\n (click)=\"openModal()\">\r\n <img src=\"assets/images/coroa.svg\" class=\"w-auto\" alt=\"icone-coroa\" />\r\n Fa\u00E7a Upgrade\r\n </button>\r\n </li>\r\n </ng-template>\r\n <ng-template #liEmpty>\r\n <li></li>\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"right\"></ng-container>\r\n\r\n <li *ngIf=\"showAssistant && (assistantService.disponivel$ | async)\" class=\"uiny-nav\">\r\n <ui-assistant-button></ui-assistant-button>\r\n </li>\r\n\r\n <li>\r\n <div class=\"mode cursor-pointer\" (click)=\"layoutToggle()\">\r\n <ui-icon pTooltip=\"Ativar Modo Escuro\" tooltipPosition=\"bottom\" icon=\"dark_mode\" size=\"24\"\r\n *ngIf=\"!dark\"></ui-icon>\r\n <ui-icon pTooltip=\"Ativar Modo Claro\" tooltipPosition=\"bottom\" icon=\"sunny\" size=\"24\"\r\n *ngIf=\"dark\"></ui-icon>\r\n\r\n </div>\r\n </li>\r\n\r\n <li class=\"maximize\">\r\n <a pTooltip=\"Maximizar\" tooltipPosition=\"bottom\" class=\"text-dark cursor-pointer\"\r\n href=\"javascript:void(0)\" (click)=\"toggleFullScreen()\">\r\n <ui-icon icon=\"fullscreen\" size=\"24\"></ui-icon>\r\n </a>\r\n </li>\r\n <li class=\"profile-nav onhover-dropdown p-0 ps-2\">\r\n <my-account [menus]=\"menus\" (onNavigate)=\"onNavigate.emit($event)\"></my-account>\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</div>\r\n<!-- Page Header Ends -->\r\n", styles: ["@charset \"UTF-8\";::ng-deep .ambiente{position:fixed;top:0;right:0;left:0;height:2px;z-index:999;text-align:center}::ng-deep .ambiente span{content:\"\";position:absolute;height:20px;width:100px;right:0;margin:auto;border-radius:0 0 0 20px;text-align:center;color:#fff;font-size:12px;padding:0}::ng-deep .ambiente.ambiente-develop{background-color:#09b409}::ng-deep .ambiente.ambiente-develop span{background-color:#09b409}::ng-deep .ambiente.ambiente-homolog{background-color:#ff5d22}::ng-deep .ambiente.ambiente-homolog span{background-color:#ff5d22}::ng-deep .image-logo{width:auto;height:20px}::ng-deep .header-logo-wrapper.col-auto.d-lg-none.px-0{height:20px}::ng-deep ui-icon.status_toggle.middle{color:#7366ff}::ng-deep ui-icon.icons-menu{height:20px}::ng-deep .mode.d-none.d-md-block{height:20px}::ng-deep .header-backgrop{position:fixed;inset:0;z-index:8}::ng-deep .header-backgrop .page-header{z-index:11!important}::ng-deep .b-color{color:#e9e7ff;background:#7366ff;height:36px;width:136px}::ng-deep .b-color img{height:15px}::ng-deep .page-wrapper .page-header.not-sidebar{margin-left:0!important;width:100%!important}::ng-deep .page-wrapper .page-header .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li{padding:0 10px!important;margin-right:0}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}@media (min-width: 577px){::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper{display:block!important}::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper img{display:block!important}}@media (max-width: 576px){::ng-deep .hide-mobile{display:none!important}}@media only screen and (max-width: 575.98px){::ng-deep .nav-menus{margin-right:0!important}::ng-deep .page-wrapper .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li{padding:0 10px!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li:last-child{padding-right:0!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}}::ng-deep body .dark-only li.maximize .text-dark span{color:#ffffffb3!important}\n"], dependencies: [{ kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: UiAssistantButtonComponent, selector: "ui-assistant-button" }, { kind: "component", type: MyAccountComponent, selector: "my-account", inputs: ["menus"], outputs: ["onNavigate"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }] });
220
+ UiHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderComponent, deps: [{ token: i1.AuthService }, { token: i1.BaseUrlService }, { token: i1.LayoutService }, { token: i1.IframeService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
221
+ UiHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiHeaderComponent, selector: "ui-header", inputs: { collapseSidebar: "collapseSidebar", fullScreen: "fullScreen", headerBackdrop: "headerBackdrop", isFree: "isFree", menus: "menus", sidebar: "sidebar", fullWidth: "fullWidth", redirectHome: "redirectHome", darkDefault: "darkDefault" }, outputs: { collapseSidebarChange: "collapseSidebarChange", fullScreenChange: "fullScreenChange", headerBackdropChange: "headerBackdropChange", onNavigate: "onNavigate", onUpgrade: "onUpgrade" }, queries: [{ propertyName: "left", first: true, predicate: ["left"], descendants: true }, { propertyName: "center", first: true, predicate: ["center"], descendants: true }, { propertyName: "right", first: true, predicate: ["right"], descendants: true }], ngImport: i0, template: "<!-- Page Header Backdrop -->\r\n<div *ngIf=\"headerBackdrop\" (click)=\"fecharBackdrop()\" class=\"header-backgrop\"></div>\r\n\r\n<!-- Page Header Start -->\r\n<div class=\"page-header\" [class.close_icon]=\"collapseSidebar\" [class.not-sidebar]=\"!sidebar\"\r\n [class.full-width]=\"fullWidth\">\r\n <div class=\"header-wrapper d-flex m-0 justify-content-between\">\r\n <div class=\"header-logo-wrapper col-auto d-lg-none px-0\">\r\n <div *ngIf=\"sidebar\" class=\"toggle-sidebar\" (click)=\"sidebarToggle()\">\r\n <ui-icon icon=\"menu\" class=\"status_toggle middle\"></ui-icon>\r\n </div>\r\n <a (click)=\"home()\" [class.cursor-pointer]=\"redirectHome\">\r\n <img class=\"img-fluid for-dark image-logo d-md-none\" src=\"assets/images/logo/logo_dark.png\" alt=\"\" />\r\n </a>\r\n </div>\r\n\r\n <div class=\"left-header horizontal-wrapper ps-0\">\r\n <ng-container [ngTemplateOutlet]=\"left\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-center\">\r\n <ng-container [ngTemplateOutlet]=\"center\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-right p-0\">\r\n <ul class=\"nav-menus\">\r\n <!-- <li class=\"language-nav\" (click)=\"languageToggle()\">\r\n <header-languages></header-languages>\r\n </li> -->\r\n <ng-container *ngIf=\"isFree; then liUpgrade; else liEmpty\"></ng-container>\r\n\r\n <ng-template #liUpgrade>\r\n <li>\r\n <button\r\n class=\"d-flex align-items-center justify-content-center border-0 rounded-pill gap-2 b-color\"\r\n (click)=\"onUpgrade.emit()\">\r\n <img src=\"assets/images/coroa.svg\" class=\"w-auto\" alt=\"icone-coroa\" />\r\n Fa\u00E7a Upgrade\r\n </button>\r\n </li>\r\n </ng-template>\r\n <ng-template #liEmpty>\r\n <li></li>\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"right\"></ng-container>\r\n\r\n <li>\r\n <div class=\"mode cursor-pointer\" (click)=\"layoutToggle()\">\r\n <ui-icon pTooltip=\"Ativar Modo Escuro\" tooltipPosition=\"bottom\" icon=\"dark_mode\" size=\"24\"\r\n *ngIf=\"!dark\"></ui-icon>\r\n <ui-icon pTooltip=\"Ativar Modo Claro\" tooltipPosition=\"bottom\" icon=\"sunny\" size=\"24\"\r\n *ngIf=\"dark\"></ui-icon>\r\n\r\n </div>\r\n </li>\r\n\r\n <li class=\"maximize\">\r\n <a pTooltip=\"Maximizar\" tooltipPosition=\"bottom\" class=\"text-dark cursor-pointer\"\r\n href=\"javascript:void(0)\" (click)=\"toggleFullScreen()\">\r\n <ui-icon icon=\"fullscreen\" size=\"24\"></ui-icon>\r\n </a>\r\n </li>\r\n <li class=\"profile-nav onhover-dropdown p-0 ps-2\">\r\n <my-account [menus]=\"menus\" (onNavigate)=\"onNavigate.emit($event)\"></my-account>\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</div>\r\n<!-- Page Header Ends -->\r\n", styles: ["@charset \"UTF-8\";::ng-deep .ambiente{position:fixed;top:0;right:0;left:0;height:2px;z-index:999;text-align:center}::ng-deep .ambiente span{content:\"\";position:absolute;height:20px;width:100px;right:0;margin:auto;border-radius:0 0 0 20px;text-align:center;color:#fff;font-size:12px;padding:0}::ng-deep .ambiente.ambiente-develop{background-color:#09b409}::ng-deep .ambiente.ambiente-develop span{background-color:#09b409}::ng-deep .ambiente.ambiente-homolog{background-color:#ff5d22}::ng-deep .ambiente.ambiente-homolog span{background-color:#ff5d22}::ng-deep .header-backgrop{position:fixed;inset:0;z-index:8}::ng-deep .header-backgrop .page-header{z-index:11!important}::ng-deep .image-logo{width:auto;height:20px}::ng-deep .header-logo-wrapper.col-auto.d-lg-none.px-0{height:20px}::ng-deep ui-icon.status_toggle.middle{color:#7366ff}::ng-deep ui-icon.icons-menu{height:20px}::ng-deep .mode.d-none.d-md-block{height:20px}::ng-deep .b-color{color:#e9e7ff;background:var(--theme-default, #7366ff);height:36px;width:136px}::ng-deep .b-color img{height:15px}::ng-deep .page-wrapper .page-header.not-sidebar,::ng-deep .page-wrapper .page-header.full-width{margin-left:0!important;width:100%!important}::ng-deep .page-wrapper .page-header .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li{padding:0 10px!important;margin-right:0}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}@media (min-width: 577px){::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper{display:block!important}::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper img{display:block!important}}@media (max-width: 576px){::ng-deep .hide-mobile{display:none!important}}@media only screen and (max-width: 575.98px){::ng-deep .nav-menus{margin-right:0!important}::ng-deep .page-wrapper .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li{padding:0 10px!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li:last-child{padding-right:0!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}}::ng-deep body .dark-only li.maximize .text-dark span{color:#ffffffb3!important}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i3.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "directive", type: i4$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: MyAccountComponent, selector: "my-account", inputs: ["menus"], outputs: ["onNavigate"] }] });
844
222
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderComponent, decorators: [{
845
223
  type: Component,
846
- args: [{ selector: 'ui-header', template: "<!-- Page Header Backdrop -->\r\n<div *ngIf=\"headerBackdrop\" (click)=\"navService.hideAllPopUps()\" class=\"header-backgrop\"></div>\r\n\r\n<!-- Page Header Start -->\r\n<div class=\"page-header\" [class.close_icon]=\"collapseSidebar\" [class.not-sidebar]=\"!sidebar\">\r\n <div class=\"header-wrapper d-flex m-0 justify-content-between\">\r\n <div class=\"header-logo-wrapper col-auto d-lg-none px-0\">\r\n <div *ngIf=\"sidebar\" class=\"toggle-sidebar\" (click)=\"sidebarToggle()\">\r\n <ui-icon icon=\"menu\" class=\"status_toggle middle\"></ui-icon>\r\n </div>\r\n <a (click)=\"home()\" [class.cursor-pointer]=\"redirectHome\">\r\n <img class=\"img-fluid for-dark image-logo d-md-none\" src=\"assets/images/logo/logo_dark.png\" alt=\"\" />\r\n </a>\r\n </div>\r\n\r\n <div class=\"left-header horizontal-wrapper ps-0\">\r\n <ng-container [ngTemplateOutlet]=\"left\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-center\">\r\n <ng-container [ngTemplateOutlet]=\"center\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-right p-0\">\r\n <ul class=\"nav-menus\">\r\n <!-- <li class=\"language-nav\" (click)=\"languageToggle()\">\r\n <header-languages></header-languages>\r\n </li> -->\r\n <ng-container *ngIf=\"isFree; then liUpgrade; else liEmpty\"></ng-container>\r\n\r\n <ng-template #liUpgrade>\r\n <li>\r\n <button\r\n class=\"d-flex align-items-center justify-content-center border-0 rounded-pill gap-2 b-color\"\r\n (click)=\"openModal()\">\r\n <img src=\"assets/images/coroa.svg\" class=\"w-auto\" alt=\"icone-coroa\" />\r\n Fa\u00E7a Upgrade\r\n </button>\r\n </li>\r\n </ng-template>\r\n <ng-template #liEmpty>\r\n <li></li>\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"right\"></ng-container>\r\n\r\n <li *ngIf=\"showAssistant && (assistantService.disponivel$ | async)\" class=\"uiny-nav\">\r\n <ui-assistant-button></ui-assistant-button>\r\n </li>\r\n\r\n <li>\r\n <div class=\"mode cursor-pointer\" (click)=\"layoutToggle()\">\r\n <ui-icon pTooltip=\"Ativar Modo Escuro\" tooltipPosition=\"bottom\" icon=\"dark_mode\" size=\"24\"\r\n *ngIf=\"!dark\"></ui-icon>\r\n <ui-icon pTooltip=\"Ativar Modo Claro\" tooltipPosition=\"bottom\" icon=\"sunny\" size=\"24\"\r\n *ngIf=\"dark\"></ui-icon>\r\n\r\n </div>\r\n </li>\r\n\r\n <li class=\"maximize\">\r\n <a pTooltip=\"Maximizar\" tooltipPosition=\"bottom\" class=\"text-dark cursor-pointer\"\r\n href=\"javascript:void(0)\" (click)=\"toggleFullScreen()\">\r\n <ui-icon icon=\"fullscreen\" size=\"24\"></ui-icon>\r\n </a>\r\n </li>\r\n <li class=\"profile-nav onhover-dropdown p-0 ps-2\">\r\n <my-account [menus]=\"menus\" (onNavigate)=\"onNavigate.emit($event)\"></my-account>\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</div>\r\n<!-- Page Header Ends -->\r\n", styles: ["@charset \"UTF-8\";::ng-deep .ambiente{position:fixed;top:0;right:0;left:0;height:2px;z-index:999;text-align:center}::ng-deep .ambiente span{content:\"\";position:absolute;height:20px;width:100px;right:0;margin:auto;border-radius:0 0 0 20px;text-align:center;color:#fff;font-size:12px;padding:0}::ng-deep .ambiente.ambiente-develop{background-color:#09b409}::ng-deep .ambiente.ambiente-develop span{background-color:#09b409}::ng-deep .ambiente.ambiente-homolog{background-color:#ff5d22}::ng-deep .ambiente.ambiente-homolog span{background-color:#ff5d22}::ng-deep .image-logo{width:auto;height:20px}::ng-deep .header-logo-wrapper.col-auto.d-lg-none.px-0{height:20px}::ng-deep ui-icon.status_toggle.middle{color:#7366ff}::ng-deep ui-icon.icons-menu{height:20px}::ng-deep .mode.d-none.d-md-block{height:20px}::ng-deep .header-backgrop{position:fixed;inset:0;z-index:8}::ng-deep .header-backgrop .page-header{z-index:11!important}::ng-deep .b-color{color:#e9e7ff;background:#7366ff;height:36px;width:136px}::ng-deep .b-color img{height:15px}::ng-deep .page-wrapper .page-header.not-sidebar{margin-left:0!important;width:100%!important}::ng-deep .page-wrapper .page-header .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li{padding:0 10px!important;margin-right:0}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}@media (min-width: 577px){::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper{display:block!important}::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper img{display:block!important}}@media (max-width: 576px){::ng-deep .hide-mobile{display:none!important}}@media only screen and (max-width: 575.98px){::ng-deep .nav-menus{margin-right:0!important}::ng-deep .page-wrapper .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li{padding:0 10px!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li:last-child{padding-right:0!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}}::ng-deep body .dark-only li.maximize .text-dark span{color:#ffffffb3!important}\n"] }]
224
+ args: [{ selector: 'ui-header', template: "<!-- Page Header Backdrop -->\r\n<div *ngIf=\"headerBackdrop\" (click)=\"fecharBackdrop()\" class=\"header-backgrop\"></div>\r\n\r\n<!-- Page Header Start -->\r\n<div class=\"page-header\" [class.close_icon]=\"collapseSidebar\" [class.not-sidebar]=\"!sidebar\"\r\n [class.full-width]=\"fullWidth\">\r\n <div class=\"header-wrapper d-flex m-0 justify-content-between\">\r\n <div class=\"header-logo-wrapper col-auto d-lg-none px-0\">\r\n <div *ngIf=\"sidebar\" class=\"toggle-sidebar\" (click)=\"sidebarToggle()\">\r\n <ui-icon icon=\"menu\" class=\"status_toggle middle\"></ui-icon>\r\n </div>\r\n <a (click)=\"home()\" [class.cursor-pointer]=\"redirectHome\">\r\n <img class=\"img-fluid for-dark image-logo d-md-none\" src=\"assets/images/logo/logo_dark.png\" alt=\"\" />\r\n </a>\r\n </div>\r\n\r\n <div class=\"left-header horizontal-wrapper ps-0\">\r\n <ng-container [ngTemplateOutlet]=\"left\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-center\">\r\n <ng-container [ngTemplateOutlet]=\"center\"></ng-container>\r\n </div>\r\n\r\n <div class=\"nav-right p-0\">\r\n <ul class=\"nav-menus\">\r\n <!-- <li class=\"language-nav\" (click)=\"languageToggle()\">\r\n <header-languages></header-languages>\r\n </li> -->\r\n <ng-container *ngIf=\"isFree; then liUpgrade; else liEmpty\"></ng-container>\r\n\r\n <ng-template #liUpgrade>\r\n <li>\r\n <button\r\n class=\"d-flex align-items-center justify-content-center border-0 rounded-pill gap-2 b-color\"\r\n (click)=\"onUpgrade.emit()\">\r\n <img src=\"assets/images/coroa.svg\" class=\"w-auto\" alt=\"icone-coroa\" />\r\n Fa\u00E7a Upgrade\r\n </button>\r\n </li>\r\n </ng-template>\r\n <ng-template #liEmpty>\r\n <li></li>\r\n </ng-template>\r\n\r\n <ng-container [ngTemplateOutlet]=\"right\"></ng-container>\r\n\r\n <li>\r\n <div class=\"mode cursor-pointer\" (click)=\"layoutToggle()\">\r\n <ui-icon pTooltip=\"Ativar Modo Escuro\" tooltipPosition=\"bottom\" icon=\"dark_mode\" size=\"24\"\r\n *ngIf=\"!dark\"></ui-icon>\r\n <ui-icon pTooltip=\"Ativar Modo Claro\" tooltipPosition=\"bottom\" icon=\"sunny\" size=\"24\"\r\n *ngIf=\"dark\"></ui-icon>\r\n\r\n </div>\r\n </li>\r\n\r\n <li class=\"maximize\">\r\n <a pTooltip=\"Maximizar\" tooltipPosition=\"bottom\" class=\"text-dark cursor-pointer\"\r\n href=\"javascript:void(0)\" (click)=\"toggleFullScreen()\">\r\n <ui-icon icon=\"fullscreen\" size=\"24\"></ui-icon>\r\n </a>\r\n </li>\r\n <li class=\"profile-nav onhover-dropdown p-0 ps-2\">\r\n <my-account [menus]=\"menus\" (onNavigate)=\"onNavigate.emit($event)\"></my-account>\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n</div>\r\n<!-- Page Header Ends -->\r\n", styles: ["@charset \"UTF-8\";::ng-deep .ambiente{position:fixed;top:0;right:0;left:0;height:2px;z-index:999;text-align:center}::ng-deep .ambiente span{content:\"\";position:absolute;height:20px;width:100px;right:0;margin:auto;border-radius:0 0 0 20px;text-align:center;color:#fff;font-size:12px;padding:0}::ng-deep .ambiente.ambiente-develop{background-color:#09b409}::ng-deep .ambiente.ambiente-develop span{background-color:#09b409}::ng-deep .ambiente.ambiente-homolog{background-color:#ff5d22}::ng-deep .ambiente.ambiente-homolog span{background-color:#ff5d22}::ng-deep .header-backgrop{position:fixed;inset:0;z-index:8}::ng-deep .header-backgrop .page-header{z-index:11!important}::ng-deep .image-logo{width:auto;height:20px}::ng-deep .header-logo-wrapper.col-auto.d-lg-none.px-0{height:20px}::ng-deep ui-icon.status_toggle.middle{color:#7366ff}::ng-deep ui-icon.icons-menu{height:20px}::ng-deep .mode.d-none.d-md-block{height:20px}::ng-deep .b-color{color:#e9e7ff;background:var(--theme-default, #7366ff);height:36px;width:136px}::ng-deep .b-color img{height:15px}::ng-deep .page-wrapper .page-header.not-sidebar,::ng-deep .page-wrapper .page-header.full-width{margin-left:0!important;width:100%!important}::ng-deep .page-wrapper .page-header .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li{padding:0 10px!important;margin-right:0}::ng-deep .page-wrapper .page-header .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}@media (min-width: 577px){::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper{display:block!important}::ng-deep .page-wrapper .page-header.not-sidebar .header-logo-wrapper img{display:block!important}}@media (max-width: 576px){::ng-deep .hide-mobile{display:none!important}}@media only screen and (max-width: 575.98px){::ng-deep .nav-menus{margin-right:0!important}::ng-deep .page-wrapper .header-wrapper{flex-direction:row}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li{padding:0 10px!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li:last-child{padding-right:0!important}::ng-deep .page-wrapper .header-wrapper .nav-right>ul li .profile-media img{width:100%!important}}::ng-deep body .dark-only li.maximize .text-dark span{color:#ffffffb3!important}\n"] }]
847
225
  }], ctorParameters: function () {
848
- return [{ type: i1.AuthService }, { type: i1.BaseUrlService }, { type: i1.LayoutService }, { type: i1.IframeService }, { type: UiAssistantService }, { type: undefined, decorators: [{
226
+ return [{ type: i1.AuthService }, { type: i1.BaseUrlService }, { type: i1.LayoutService }, { type: i1.IframeService }, { type: undefined, decorators: [{
849
227
  type: Inject,
850
228
  args: [DOCUMENT]
851
229
  }] }];
@@ -861,12 +239,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
861
239
  type: Input
862
240
  }], sidebar: [{
863
241
  type: Input
242
+ }], fullWidth: [{
243
+ type: Input
864
244
  }], redirectHome: [{
865
245
  type: Input
866
246
  }], darkDefault: [{
867
247
  type: Input
868
- }], showAssistant: [{
869
- type: Input
870
248
  }], collapseSidebarChange: [{
871
249
  type: Output
872
250
  }], fullScreenChange: [{
@@ -875,6 +253,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
875
253
  type: Output
876
254
  }], onNavigate: [{
877
255
  type: Output
256
+ }], onUpgrade: [{
257
+ type: Output
878
258
  }], left: [{
879
259
  type: ContentChild,
880
260
  args: ['left']
@@ -886,1165 +266,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
886
266
  args: ['right']
887
267
  }] } });
888
268
 
889
- const ROTULOS = {
890
- pendente: 'Aguardando sua confirmação',
891
- executando: 'Executando…',
892
- concluida: 'Concluída',
893
- cancelada: 'Cancelada',
894
- expirada: 'Expirada',
895
- invalidada: 'Precisa de nova confirmação',
896
- falha: 'Não concluída',
897
- indeterminada: 'Resultado a verificar',
898
- };
899
- const DESCRICOES = {
900
- concluida: 'A alteração foi aplicada.',
901
- cancelada: 'Nada foi alterado.',
902
- expirada: 'O prazo para confirmar terminou. Nada foi alterado; peça de novo se ainda quiser.',
903
- invalidada: 'Os dados mudaram desde a proposta. Peça a alteração de novo para revisar.',
904
- falha: 'A alteração não foi aplicada.',
905
- };
906
- /**
907
- * Card de ação pendente. A confirmação só acontece pelo clique em "Confirmar":
908
- * o card emite apenas o id da ação persistida, sem argumentos.
909
- */
910
- class UiAssistantActionCardComponent {
911
- constructor(cdr, zone) {
912
- this.cdr = cdr;
913
- this.zone = zone;
914
- this.confirmar = new EventEmitter();
915
- this.cancelar = new EventEmitter();
916
- this.verificar = new EventEmitter();
917
- this.agora = Date.now();
918
- }
919
- ngOnChanges() {
920
- this.agendar();
921
- }
922
- ngOnDestroy() {
923
- clearInterval(this.timer);
924
- }
925
- /** O servidor decide a expiração; aqui só evitamos oferecer um clique que já vai falhar. */
926
- get expiradaLocalmente() {
927
- var _a;
928
- return ((_a = this.acao) === null || _a === void 0 ? void 0 : _a.estado) === 'pendente' && !!this.acao.expiraEm && this.acao.expiraEm.getTime() <= this.agora;
929
- }
930
- get estado() {
931
- var _a;
932
- return this.expiradaLocalmente ? 'expirada' : (_a = this.acao) === null || _a === void 0 ? void 0 : _a.estado;
933
- }
934
- get rotulo() {
935
- var _a;
936
- return (_a = ROTULOS[this.estado]) !== null && _a !== void 0 ? _a : '';
937
- }
938
- get descricao() {
939
- var _a;
940
- return ((_a = this.acao) === null || _a === void 0 ? void 0 : _a.aviso) || DESCRICOES[this.estado] || null;
941
- }
942
- get podeDecidir() {
943
- return this.estado === 'pendente' && !this.acao.enviando;
944
- }
945
- get minutosRestantes() {
946
- if (this.estado !== 'pendente' || !this.acao.expiraEm)
947
- return null;
948
- return Math.max(1, Math.ceil((this.acao.expiraEm.getTime() - this.agora) / 60000));
949
- }
950
- ehCor(valor) {
951
- return /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(valor);
952
- }
953
- aoConfirmar() {
954
- if (this.podeDecidir)
955
- this.confirmar.emit(this.acao.id);
956
- }
957
- aoCancelar() {
958
- if (this.podeDecidir)
959
- this.cancelar.emit(this.acao.id);
960
- }
961
- agendar() {
962
- var _a;
963
- clearInterval(this.timer);
964
- this.agora = Date.now();
965
- if (((_a = this.acao) === null || _a === void 0 ? void 0 : _a.estado) !== 'pendente' || !this.acao.expiraEm)
966
- return;
967
- this.zone.runOutsideAngular(() => {
968
- this.timer = setInterval(() => this.zone.run(() => {
969
- this.agora = Date.now();
970
- if (this.expiradaLocalmente)
971
- clearInterval(this.timer);
972
- this.cdr.markForCheck();
973
- }), 15000);
974
- });
975
- }
976
- }
977
- UiAssistantActionCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantActionCardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
978
- UiAssistantActionCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantActionCardComponent, selector: "ui-assistant-action-card", inputs: { acao: "acao" }, outputs: { confirmar: "confirmar", cancelar: "cancelar", verificar: "verificar" }, usesOnChanges: true, ngImport: i0, template: "<article class=\"uiny-acao\" [ngClass]=\"'uiny-acao--' + estado\" [attr.aria-busy]=\"acao.enviando ? 'true' : null\"\n [attr.aria-labelledby]=\"'uiny-acao-' + acao.id\">\n <header class=\"uiny-acao__topo\">\n <span class=\"uiny-acao__selo\">\n <span *ngIf=\"estado === 'executando' || acao.enviando\" class=\"uiny-acao__spinner\" aria-hidden=\"true\"></span>\n <ui-icon *ngIf=\"estado === 'concluida'\" icon=\"check_circle\" size=\"16\"></ui-icon>\n <ui-icon *ngIf=\"estado === 'falha' || estado === 'indeterminada'\" icon=\"error\" size=\"16\"></ui-icon>\n <ui-icon *ngIf=\"estado === 'pendente' && !acao.enviando\" icon=\"pending_actions\" size=\"16\"></ui-icon>\n {{ rotulo }}\n </span>\n <span *ngIf=\"minutosRestantes\" class=\"uiny-acao__prazo\">expira em {{ minutosRestantes }} min</span>\n </header>\n\n <p class=\"uiny-acao__resumo\" [id]=\"'uiny-acao-' + acao.id\">{{ acao.resumo }}</p>\n\n <dl *ngIf=\"acao.detalhes?.length\" class=\"uiny-acao__detalhes\">\n <div *ngFor=\"let d of acao.detalhes\" class=\"uiny-acao__item\">\n <dt>{{ d.rotulo }}</dt>\n <dd>\n <span *ngIf=\"ehCor(d.valor)\" class=\"uiny-acao__cor\" [style.background-color]=\"d.valor\" aria-hidden=\"true\"></span>\n {{ d.valor }}\n </dd>\n </div>\n </dl>\n\n <p *ngIf=\"descricao\" class=\"uiny-acao__descricao\" role=\"status\">{{ descricao }}</p>\n\n <div *ngIf=\"estado === 'pendente'\" class=\"uiny-acao__botoes\">\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--secundario\" [disabled]=\"!podeDecidir\" (click)=\"aoCancelar()\">\n Cancelar\n </button>\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--primario\" [disabled]=\"!podeDecidir\" (click)=\"aoConfirmar()\">\n Confirmar\n </button>\n </div>\n\n <div *ngIf=\"estado === 'indeterminada'\" class=\"uiny-acao__botoes\">\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--secundario\" [disabled]=\"acao.enviando\" (click)=\"verificar.emit(acao.id)\">\n Verificar resultado\n </button>\n </div>\n</article>\n", styles: [":host{display:block;margin-top:8px}.uiny-acao{padding:12px 14px;border:1px solid var(--uiny-borda, #ececf3);border-left:3px solid var(--uiny-primaria, #7366ff);border-radius:12px;background:var(--uiny-fundo, #fff);box-shadow:0 2px 10px #1f1b4d0d}.uiny-acao--concluida{border-left-color:#1aa467}.uiny-acao--falha,.uiny-acao--indeterminada{border-left-color:var(--uiny-erro, #d9364f)}.uiny-acao--cancelada,.uiny-acao--expirada,.uiny-acao--invalidada{border-left-color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__topo{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.uiny-acao__selo{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;color:var(--uiny-primaria, #7366ff)}.uiny-acao--concluida .uiny-acao__selo{color:#1aa467}.uiny-acao--falha .uiny-acao__selo,.uiny-acao--indeterminada .uiny-acao__selo{color:var(--uiny-erro, #d9364f)}.uiny-acao--cancelada .uiny-acao__selo,.uiny-acao--expirada .uiny-acao__selo,.uiny-acao--invalidada .uiny-acao__selo{color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__prazo{font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__resumo{margin:0;font-weight:600;line-height:1.45;color:var(--uiny-texto, #2b2b2b);overflow-wrap:anywhere}.uiny-acao__detalhes{margin:10px 0 0;padding:8px 10px;border-radius:9px;background:var(--uiny-fundo-suave, #f7f7fb);font-size:13px}.uiny-acao__item{display:flex;justify-content:space-between;gap:12px}.uiny-acao__item+.uiny-acao__item{margin-top:4px}.uiny-acao__item dt{font-weight:400;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__item dd{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:600;text-align:right;overflow-wrap:anywhere}.uiny-acao__cor{flex:0 0 auto;width:14px;height:14px;border:1px solid rgba(0,0,0,.12);border-radius:4px}.uiny-acao__descricao{margin:8px 0 0;font-size:12.5px;line-height:1.45;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__botoes{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.uiny-acao__botao{min-height:34px;padding:0 14px;border-radius:9px;font-size:13px;font-weight:600;cursor:pointer}.uiny-acao__botao:disabled{opacity:.5;cursor:not-allowed}.uiny-acao__botao:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-acao__botao--primario{border:1px solid var(--uiny-primaria, #7366ff);background:var(--uiny-primaria, #7366ff);color:#fff}.uiny-acao__botao--secundario{border:1px solid var(--uiny-borda, #ececf3);background:transparent;color:var(--uiny-texto, #2b2b2b)}.uiny-acao__spinner{width:12px;height:12px;border:2px solid var(--uiny-borda, #ececf3);border-top-color:var(--uiny-primaria, #7366ff);border-radius:50%;animation:uiny-girar .8s linear infinite}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny-acao__spinner{animation-duration:2.4s}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
979
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantActionCardComponent, decorators: [{
980
- type: Component,
981
- args: [{ selector: 'ui-assistant-action-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<article class=\"uiny-acao\" [ngClass]=\"'uiny-acao--' + estado\" [attr.aria-busy]=\"acao.enviando ? 'true' : null\"\n [attr.aria-labelledby]=\"'uiny-acao-' + acao.id\">\n <header class=\"uiny-acao__topo\">\n <span class=\"uiny-acao__selo\">\n <span *ngIf=\"estado === 'executando' || acao.enviando\" class=\"uiny-acao__spinner\" aria-hidden=\"true\"></span>\n <ui-icon *ngIf=\"estado === 'concluida'\" icon=\"check_circle\" size=\"16\"></ui-icon>\n <ui-icon *ngIf=\"estado === 'falha' || estado === 'indeterminada'\" icon=\"error\" size=\"16\"></ui-icon>\n <ui-icon *ngIf=\"estado === 'pendente' && !acao.enviando\" icon=\"pending_actions\" size=\"16\"></ui-icon>\n {{ rotulo }}\n </span>\n <span *ngIf=\"minutosRestantes\" class=\"uiny-acao__prazo\">expira em {{ minutosRestantes }} min</span>\n </header>\n\n <p class=\"uiny-acao__resumo\" [id]=\"'uiny-acao-' + acao.id\">{{ acao.resumo }}</p>\n\n <dl *ngIf=\"acao.detalhes?.length\" class=\"uiny-acao__detalhes\">\n <div *ngFor=\"let d of acao.detalhes\" class=\"uiny-acao__item\">\n <dt>{{ d.rotulo }}</dt>\n <dd>\n <span *ngIf=\"ehCor(d.valor)\" class=\"uiny-acao__cor\" [style.background-color]=\"d.valor\" aria-hidden=\"true\"></span>\n {{ d.valor }}\n </dd>\n </div>\n </dl>\n\n <p *ngIf=\"descricao\" class=\"uiny-acao__descricao\" role=\"status\">{{ descricao }}</p>\n\n <div *ngIf=\"estado === 'pendente'\" class=\"uiny-acao__botoes\">\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--secundario\" [disabled]=\"!podeDecidir\" (click)=\"aoCancelar()\">\n Cancelar\n </button>\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--primario\" [disabled]=\"!podeDecidir\" (click)=\"aoConfirmar()\">\n Confirmar\n </button>\n </div>\n\n <div *ngIf=\"estado === 'indeterminada'\" class=\"uiny-acao__botoes\">\n <button type=\"button\" class=\"uiny-acao__botao uiny-acao__botao--secundario\" [disabled]=\"acao.enviando\" (click)=\"verificar.emit(acao.id)\">\n Verificar resultado\n </button>\n </div>\n</article>\n", styles: [":host{display:block;margin-top:8px}.uiny-acao{padding:12px 14px;border:1px solid var(--uiny-borda, #ececf3);border-left:3px solid var(--uiny-primaria, #7366ff);border-radius:12px;background:var(--uiny-fundo, #fff);box-shadow:0 2px 10px #1f1b4d0d}.uiny-acao--concluida{border-left-color:#1aa467}.uiny-acao--falha,.uiny-acao--indeterminada{border-left-color:var(--uiny-erro, #d9364f)}.uiny-acao--cancelada,.uiny-acao--expirada,.uiny-acao--invalidada{border-left-color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__topo{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.uiny-acao__selo{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;color:var(--uiny-primaria, #7366ff)}.uiny-acao--concluida .uiny-acao__selo{color:#1aa467}.uiny-acao--falha .uiny-acao__selo,.uiny-acao--indeterminada .uiny-acao__selo{color:var(--uiny-erro, #d9364f)}.uiny-acao--cancelada .uiny-acao__selo,.uiny-acao--expirada .uiny-acao__selo,.uiny-acao--invalidada .uiny-acao__selo{color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__prazo{font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__resumo{margin:0;font-weight:600;line-height:1.45;color:var(--uiny-texto, #2b2b2b);overflow-wrap:anywhere}.uiny-acao__detalhes{margin:10px 0 0;padding:8px 10px;border-radius:9px;background:var(--uiny-fundo-suave, #f7f7fb);font-size:13px}.uiny-acao__item{display:flex;justify-content:space-between;gap:12px}.uiny-acao__item+.uiny-acao__item{margin-top:4px}.uiny-acao__item dt{font-weight:400;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__item dd{display:inline-flex;align-items:center;gap:6px;margin:0;font-weight:600;text-align:right;overflow-wrap:anywhere}.uiny-acao__cor{flex:0 0 auto;width:14px;height:14px;border:1px solid rgba(0,0,0,.12);border-radius:4px}.uiny-acao__descricao{margin:8px 0 0;font-size:12.5px;line-height:1.45;color:var(--uiny-texto-suave, #6b6b80)}.uiny-acao__botoes{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.uiny-acao__botao{min-height:34px;padding:0 14px;border-radius:9px;font-size:13px;font-weight:600;cursor:pointer}.uiny-acao__botao:disabled{opacity:.5;cursor:not-allowed}.uiny-acao__botao:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-acao__botao--primario{border:1px solid var(--uiny-primaria, #7366ff);background:var(--uiny-primaria, #7366ff);color:#fff}.uiny-acao__botao--secundario{border:1px solid var(--uiny-borda, #ececf3);background:transparent;color:var(--uiny-texto, #2b2b2b)}.uiny-acao__spinner{width:12px;height:12px;border:2px solid var(--uiny-borda, #ececf3);border-top-color:var(--uiny-primaria, #7366ff);border-radius:50%;animation:uiny-girar .8s linear infinite}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny-acao__spinner{animation-duration:2.4s}}\n"] }]
982
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }]; }, propDecorators: { acao: [{
983
- type: Input
984
- }], confirmar: [{
985
- type: Output
986
- }], cancelar: [{
987
- type: Output
988
- }], verificar: [{
989
- type: Output
990
- }] } });
991
-
992
- const ALTURA_MAXIMA = 160;
993
- /** Campo de mensagem: Enter envia, Shift+Enter quebra linha, cresce até ALTURA_MAXIMA. */
994
- class UiAssistantComposerComponent {
995
- constructor() {
996
- this.desabilitado = false;
997
- this.enviando = false;
998
- /** Explica por que o envio está bloqueado (sem internet, carregando...). */
999
- this.motivo = null;
1000
- this.enviar = new EventEmitter();
1001
- this.max = UINY_MENSAGEM_MAX_CARACTERES;
1002
- this.texto = '';
1003
- }
1004
- get vazio() {
1005
- return !this.texto.trim();
1006
- }
1007
- get bloqueado() {
1008
- return this.desabilitado || this.enviando || this.vazio || this.texto.length > this.max;
1009
- }
1010
- get mostrarContador() {
1011
- return this.texto.length > this.max - 400;
1012
- }
1013
- focar() {
1014
- var _a;
1015
- (_a = this.campo) === null || _a === void 0 ? void 0 : _a.nativeElement.focus({ preventScroll: true });
1016
- }
1017
- limpar() {
1018
- this.texto = '';
1019
- if (this.campo) {
1020
- this.campo.nativeElement.value = '';
1021
- this.ajustarAltura();
1022
- }
1023
- }
1024
- aoDigitar(valor) {
1025
- this.texto = valor;
1026
- this.ajustarAltura();
1027
- }
1028
- aoTeclar(evento) {
1029
- if (evento.key !== 'Enter' || evento.shiftKey)
1030
- return;
1031
- // durante a composição (IME, acentos em alguns teclados) Enter confirma o caractere
1032
- if (evento.isComposing || evento.keyCode === 229)
1033
- return;
1034
- evento.preventDefault();
1035
- this.submeter();
1036
- }
1037
- submeter() {
1038
- if (this.bloqueado)
1039
- return;
1040
- this.enviar.emit(this.texto.trim());
1041
- }
1042
- ajustarAltura() {
1043
- const el = this.campo.nativeElement;
1044
- el.style.height = 'auto';
1045
- el.style.height = Math.min(el.scrollHeight, ALTURA_MAXIMA) + 'px';
1046
- el.style.overflowY = el.scrollHeight > ALTURA_MAXIMA ? 'auto' : 'hidden';
1047
- }
1048
- }
1049
- UiAssistantComposerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1050
- UiAssistantComposerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantComposerComponent, selector: "ui-assistant-composer", inputs: { desabilitado: "desabilitado", enviando: "enviando", motivo: "motivo" }, outputs: { enviar: "enviar" }, viewQueries: [{ propertyName: "campo", first: true, predicate: ["campo"], descendants: true, static: true }], ngImport: i0, template: "<form class=\"uiny-composer\" (submit)=\"$event.preventDefault(); submeter()\">\n <label for=\"uiny-campo\" class=\"uiny-sr\">Mensagem para a Uiny</label>\n <div class=\"uiny-composer__caixa\" [class.is-desabilitado]=\"desabilitado\">\n <textarea #campo id=\"uiny-campo\" class=\"uiny-composer__campo\" rows=\"1\"\n placeholder=\"Pergunte algo para a Uiny\"\n [value]=\"texto\"\n [attr.aria-describedby]=\"motivo ? 'uiny-campo-motivo' : 'uiny-campo-dica'\"\n [attr.maxlength]=\"max + 200\"\n (input)=\"aoDigitar(campo.value)\"\n (keydown)=\"aoTeclar($event)\"></textarea>\n\n <button type=\"submit\" class=\"uiny-composer__enviar\" [disabled]=\"bloqueado\"\n [attr.aria-label]=\"enviando ? 'Aguardando a resposta da Uiny' : 'Enviar mensagem'\"\n [title]=\"enviando ? 'Aguardando a resposta' : 'Enviar (Enter)'\">\n <span *ngIf=\"enviando\" class=\"uiny-composer__spinner\" aria-hidden=\"true\"></span>\n <ui-icon *ngIf=\"!enviando\" icon=\"arrow_upward\" size=\"20\"></ui-icon>\n </button>\n </div>\n\n <div class=\"uiny-composer__rodape\">\n <span *ngIf=\"motivo\" id=\"uiny-campo-motivo\" class=\"uiny-composer__motivo\">{{ motivo }}</span>\n <span *ngIf=\"!motivo\" id=\"uiny-campo-dica\">Enter envia \u00B7 Shift+Enter quebra a linha</span>\n <span *ngIf=\"mostrarContador\" class=\"uiny-composer__contador\" [class.is-excedido]=\"texto.length > max\">\n {{ texto.length }}/{{ max }}\n </span>\n </div>\n</form>\n", styles: [":host{display:block;padding:10px 12px 12px;border-top:1px solid var(--uiny-borda, #ececf3);background:var(--uiny-fundo, #fff)}.uiny-composer__caixa{display:flex;align-items:flex-end;gap:8px;padding:6px 6px 6px 12px;border:1px solid var(--uiny-borda, #ececf3);border-radius:14px;background:var(--uiny-fundo-suave, #f7f7fb);transition:border-color .15s ease,box-shadow .15s ease}.uiny-composer__caixa:focus-within{border-color:var(--uiny-primaria, #7366ff);box-shadow:0 0 0 3px #7366ff26}.uiny-composer__caixa.is-desabilitado{opacity:.75}.uiny-composer__campo{flex:1 1 auto;min-width:0;max-height:160px;padding:6px 0;border:0;outline:0;background:transparent;color:var(--uiny-texto, #2b2b2b);font:inherit;font-size:14px;line-height:1.45;resize:none;overflow-y:hidden}.uiny-composer__campo::placeholder{color:var(--uiny-texto-suave, #6b6b80)}.uiny-composer__enviar{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:0;border-radius:10px;background:var(--uiny-primaria, #7366ff);color:#fff;cursor:pointer;transition:opacity .15s ease}.uiny-composer__enviar:disabled{opacity:.35;cursor:not-allowed}.uiny-composer__enviar:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-composer__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny-composer__rodape{display:flex;justify-content:space-between;gap:8px;margin-top:6px;padding:0 4px;font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-composer__motivo{color:var(--uiny-offline-texto, #8a5300)}.uiny-composer__contador.is-excedido{color:var(--uiny-erro, #d9364f);font-weight:600}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny-composer__caixa,.uiny-composer__enviar{transition:none}.uiny-composer__spinner{animation-duration:2.4s}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1051
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantComposerComponent, decorators: [{
1052
- type: Component,
1053
- args: [{ selector: 'ui-assistant-composer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<form class=\"uiny-composer\" (submit)=\"$event.preventDefault(); submeter()\">\n <label for=\"uiny-campo\" class=\"uiny-sr\">Mensagem para a Uiny</label>\n <div class=\"uiny-composer__caixa\" [class.is-desabilitado]=\"desabilitado\">\n <textarea #campo id=\"uiny-campo\" class=\"uiny-composer__campo\" rows=\"1\"\n placeholder=\"Pergunte algo para a Uiny\"\n [value]=\"texto\"\n [attr.aria-describedby]=\"motivo ? 'uiny-campo-motivo' : 'uiny-campo-dica'\"\n [attr.maxlength]=\"max + 200\"\n (input)=\"aoDigitar(campo.value)\"\n (keydown)=\"aoTeclar($event)\"></textarea>\n\n <button type=\"submit\" class=\"uiny-composer__enviar\" [disabled]=\"bloqueado\"\n [attr.aria-label]=\"enviando ? 'Aguardando a resposta da Uiny' : 'Enviar mensagem'\"\n [title]=\"enviando ? 'Aguardando a resposta' : 'Enviar (Enter)'\">\n <span *ngIf=\"enviando\" class=\"uiny-composer__spinner\" aria-hidden=\"true\"></span>\n <ui-icon *ngIf=\"!enviando\" icon=\"arrow_upward\" size=\"20\"></ui-icon>\n </button>\n </div>\n\n <div class=\"uiny-composer__rodape\">\n <span *ngIf=\"motivo\" id=\"uiny-campo-motivo\" class=\"uiny-composer__motivo\">{{ motivo }}</span>\n <span *ngIf=\"!motivo\" id=\"uiny-campo-dica\">Enter envia \u00B7 Shift+Enter quebra a linha</span>\n <span *ngIf=\"mostrarContador\" class=\"uiny-composer__contador\" [class.is-excedido]=\"texto.length > max\">\n {{ texto.length }}/{{ max }}\n </span>\n </div>\n</form>\n", styles: [":host{display:block;padding:10px 12px 12px;border-top:1px solid var(--uiny-borda, #ececf3);background:var(--uiny-fundo, #fff)}.uiny-composer__caixa{display:flex;align-items:flex-end;gap:8px;padding:6px 6px 6px 12px;border:1px solid var(--uiny-borda, #ececf3);border-radius:14px;background:var(--uiny-fundo-suave, #f7f7fb);transition:border-color .15s ease,box-shadow .15s ease}.uiny-composer__caixa:focus-within{border-color:var(--uiny-primaria, #7366ff);box-shadow:0 0 0 3px #7366ff26}.uiny-composer__caixa.is-desabilitado{opacity:.75}.uiny-composer__campo{flex:1 1 auto;min-width:0;max-height:160px;padding:6px 0;border:0;outline:0;background:transparent;color:var(--uiny-texto, #2b2b2b);font:inherit;font-size:14px;line-height:1.45;resize:none;overflow-y:hidden}.uiny-composer__campo::placeholder{color:var(--uiny-texto-suave, #6b6b80)}.uiny-composer__enviar{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:0;border-radius:10px;background:var(--uiny-primaria, #7366ff);color:#fff;cursor:pointer;transition:opacity .15s ease}.uiny-composer__enviar:disabled{opacity:.35;cursor:not-allowed}.uiny-composer__enviar:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-composer__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny-composer__rodape{display:flex;justify-content:space-between;gap:8px;margin-top:6px;padding:0 4px;font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-composer__motivo{color:var(--uiny-offline-texto, #8a5300)}.uiny-composer__contador.is-excedido{color:var(--uiny-erro, #d9364f);font-weight:600}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny-composer__caixa,.uiny-composer__enviar{transition:none}.uiny-composer__spinner{animation-duration:2.4s}}\n"] }]
1054
- }], propDecorators: { desabilitado: [{
1055
- type: Input
1056
- }], enviando: [{
1057
- type: Input
1058
- }], motivo: [{
1059
- type: Input
1060
- }], enviar: [{
1061
- type: Output
1062
- }], campo: [{
1063
- type: ViewChild,
1064
- args: ['campo', { static: true }]
1065
- }] } });
1066
-
1067
- const ESTADO_INICIAL = {
1068
- conversa: null,
1069
- mensagens: [],
1070
- acoes: {},
1071
- enviando: false,
1072
- carregandoConversa: false,
1073
- erro: null,
1074
- aviso: null,
1075
- conversas: null,
1076
- carregandoConversas: false,
1077
- textoComFalha: null,
1078
- };
1079
- /**
1080
- * Estado da conversa, só em memória. Nada vai para localStorage/sessionStorage.
1081
- *
1082
- * Limpeza: qualquer mudança de sessão (logout, outro usuário, outra empresa, novo token,
1083
- * 401 da API) ou a Uiny ficar indisponível cancela as requisições em andamento e
1084
- * descarta todo o estado.
1085
- */
1086
- class UinyChatService {
1087
- constructor(api, session, assistant) {
1088
- this.api = api;
1089
- this.session = session;
1090
- this.assistant = assistant;
1091
- this.estado$ = new BehaviorSubject(ESTADO_INICIAL);
1092
- /** Emite para abortar as requisições da sessão/conversa atual. */
1093
- this.cancelarConversa$ = new Subject();
1094
- this.cancelarSessao$ = new Subject();
1095
- this.anuncios$ = new Subject();
1096
- this.subs = new Subscription();
1097
- this.sessao = null;
1098
- this.idLocal = 0;
1099
- /** Loja usada ao criar a conversa atual: o backend restringe a conversa a ela. */
1100
- this.escopoLoja = null;
1101
- this.estado = this.estado$.asObservable();
1102
- /** Textos curtos para a região aria-live do painel. */
1103
- this.anuncios = this.anuncios$.asObservable();
1104
- this.subs.add(this.session.sessao$.subscribe(sessao => {
1105
- var _a;
1106
- if ((sessao === null || sessao === void 0 ? void 0 : sessao.id) !== ((_a = this.sessao) === null || _a === void 0 ? void 0 : _a.id)) {
1107
- this.sessao = sessao;
1108
- this.limparTudo();
1109
- }
1110
- }));
1111
- this.subs.add(this.assistant.disponivel$.pipe(filter(d => !d)).subscribe(() => this.limparTudo()));
1112
- // Trocar de loja muda o escopo da conversa: não reaproveitamos a anterior.
1113
- this.subs.add(this.assistant.contexto$.pipe(map(c => { var _a; return (_a = c.idLoja) !== null && _a !== void 0 ? _a : null; }), distinctUntilChanged()).subscribe(idLoja => {
1114
- if (this.valor.conversa && this.escopoLoja !== idLoja) {
1115
- this.encerrarConversa('Você mudou de loja. Começamos uma nova conversa para manter os assuntos separados.');
1116
- }
1117
- }));
1118
- }
1119
- ngOnDestroy() {
1120
- this.cancelarSessao$.next();
1121
- this.subs.unsubscribe();
1122
- }
1123
- get valor() {
1124
- return this.estado$.value;
1125
- }
1126
- get ocupado() {
1127
- const v = this.valor;
1128
- return v.enviando || Object.values(v.acoes).some(a => a.enviando || a.estado === 'executando');
1129
- }
1130
- enviar(textoBruto) {
1131
- const texto = (textoBruto || '').trim();
1132
- const sistema = this.assistant.sistemaOrigem;
1133
- if (!texto || !sistema || !this.sessao || this.valor.enviando)
1134
- return false;
1135
- if (texto.length > UINY_MENSAGEM_MAX_CARACTERES) {
1136
- this.patch({ erro: this.erroLocal('invalido', `A mensagem pode ter até ${UINY_MENSAGEM_MAX_CARACTERES} caracteres.`) });
1137
- return false;
1138
- }
1139
- const contexto = contextoParaApi(sistema, this.assistant.contexto);
1140
- const local = {
1141
- id: this.novoIdLocal(), papel: 'usuario', estado: null, texto, idAcao: null, opcoes: [], criadaEm: new Date(),
1142
- };
1143
- this.patch({
1144
- mensagens: [...this.valor.mensagens.filter(m => !m.erro), local],
1145
- enviando: true,
1146
- erro: null,
1147
- aviso: null,
1148
- textoComFalha: null,
1149
- });
1150
- this.anuncios$.next('A Uiny está respondendo…');
1151
- const conversa$ = this.valor.conversa
1152
- ? of(this.valor.conversa)
1153
- : this.api.criarConversa(contexto).pipe(map(r => {
1154
- var _a;
1155
- const conversa = adaptarConversa(r === null || r === void 0 ? void 0 : r.conversa);
1156
- if (!conversa)
1157
- throw this.erroLocal('servidor', 'Não foi possível iniciar a conversa.');
1158
- this.escopoLoja = (_a = contexto.id_loja) !== null && _a !== void 0 ? _a : null;
1159
- this.patch({ conversa, conversas: null });
1160
- return conversa;
1161
- }));
1162
- conversa$.pipe(switchMap(conversa => this.api.enviarMensagem(conversa.id, texto, contexto)), takeUntil(this.cancelarConversa$), finalize(() => this.valor.enviando && this.patch({ enviando: false }))).subscribe({
1163
- next: resposta => {
1164
- this.aplicarResposta(resposta, true);
1165
- this.patch({ enviando: false });
1166
- this.anuncios$.next((resposta === null || resposta === void 0 ? void 0 : resposta.mensagem) ? 'Nova resposta da Uiny.' : '');
1167
- },
1168
- error: err => {
1169
- var _a;
1170
- const erro = this.normalizar(err);
1171
- // erros da API podem trazer a mensagem da Uiny (ex.: falha do provedor registrada)
1172
- if ((_a = erro.corpo) === null || _a === void 0 ? void 0 : _a.mensagem)
1173
- this.aplicarResposta(erro.corpo, false);
1174
- this.patch({
1175
- mensagens: this.valor.mensagens.map(m => m.id === local.id ? Object.assign(Object.assign({}, m), { erro: true }) : m),
1176
- enviando: false,
1177
- erro,
1178
- textoComFalha: erro.tipo === 'sessao' ? null : texto,
1179
- });
1180
- this.anuncios$.next(erro.mensagem);
1181
- },
1182
- });
1183
- return true;
1184
- }
1185
- /** Começa do zero. Bloqueado enquanto há envio ou ação em execução. */
1186
- novaConversa() {
1187
- if (this.ocupado)
1188
- return false;
1189
- this.encerrarConversa(null);
1190
- return true;
1191
- }
1192
- listarConversas() {
1193
- if (!this.sessao || this.valor.carregandoConversas)
1194
- return;
1195
- this.patch({ carregandoConversas: true, erro: null });
1196
- this.api.listarConversas().pipe(takeUntil(this.cancelarSessao$), finalize(() => this.patch({ carregandoConversas: false }))).subscribe({
1197
- next: r => this.patch({
1198
- conversas: (Array.isArray(r === null || r === void 0 ? void 0 : r.data) ? r.data : []).map(adaptarConversa).filter(c => !!c && this.doContextoAtual(c)),
1199
- }),
1200
- error: err => this.patch({ erro: this.normalizar(err), conversas: [] }),
1201
- });
1202
- }
1203
- abrirConversa(id) {
1204
- var _a;
1205
- const resumo = (_a = this.valor.conversas) === null || _a === void 0 ? void 0 : _a.find(c => c.id === id);
1206
- if (!resumo || this.ocupado || !this.doContextoAtual(resumo))
1207
- return;
1208
- this.encerrarConversa(null);
1209
- this.patch({ carregandoConversa: true });
1210
- this.api.historico(id).pipe(takeUntil(this.cancelarConversa$), finalize(() => this.patch({ carregandoConversa: false }))).subscribe({
1211
- next: r => {
1212
- var _a;
1213
- const conversa = (_a = adaptarConversa(r === null || r === void 0 ? void 0 : r.conversa)) !== null && _a !== void 0 ? _a : resumo;
1214
- if (!this.doContextoAtual(conversa)) {
1215
- this.patch({ aviso: 'Essa conversa pertence a outro contexto e não pode ser continuada aqui.' });
1216
- return;
1217
- }
1218
- this.escopoLoja = conversa.idLoja;
1219
- const mensagensApi = Array.isArray(r === null || r === void 0 ? void 0 : r.data) ? r.data : [];
1220
- this.patch({
1221
- conversa,
1222
- mensagens: mensagensApi.map(adaptarMensagem).filter(Boolean),
1223
- acoes: this.mesclarAcoes(mensagensApi.map(m => m === null || m === void 0 ? void 0 : m.acao_pendente)),
1224
- });
1225
- // Histórico não reemite instruções de interface: elas já foram tratadas quando chegaram.
1226
- },
1227
- error: err => this.patch({ erro: this.normalizar(err) }),
1228
- });
1229
- }
1230
- /**
1231
- * Confirma pelo id da ação persistida no backend. Só é chamado pelo clique no card;
1232
- * nenhum texto da conversa dispara isto. Nunca há repetição automática.
1233
- */
1234
- confirmar(idAcao) {
1235
- const acao = this.valor.acoes[idAcao];
1236
- if (!acao || acao.enviando || acao.estado !== 'pendente')
1237
- return;
1238
- if (acao.expiraEm && acao.expiraEm.getTime() <= Date.now()) {
1239
- this.atualizarAcao(idAcao, { estado: 'expirada', aviso: 'O prazo para confirmar terminou. Peça a alteração de novo.' });
1240
- return;
1241
- }
1242
- this.atualizarAcao(idAcao, { enviando: true, estado: 'executando', aviso: null });
1243
- this.anuncios$.next('Executando a alteração…');
1244
- this.api.confirmarAcao(idAcao).pipe(takeUntil(this.cancelarSessao$)).subscribe({
1245
- next: resposta => this.concluirDecisao(idAcao, resposta, null, true),
1246
- error: err => {
1247
- var _a, _b;
1248
- const erro = this.normalizar(err);
1249
- if (((_a = erro.corpo) === null || _a === void 0 ? void 0 : _a.acao_pendente) || ((_b = erro.corpo) === null || _b === void 0 ? void 0 : _b.acao_anterior)) {
1250
- // 409 (expirada, invalidada com nova proposta, em execução) ou 502/504: o corpo traz o estado real
1251
- this.concluirDecisao(idAcao, erro.corpo, erro, true);
1252
- }
1253
- else if (erro.tipo === 'timeout' || erro.tipo === 'rede' || erro.tipo === 'servidor') {
1254
- // A operação pode ter sido executada. Não repetimos: o usuário verifica o estado.
1255
- this.atualizarAcao(idAcao, {
1256
- enviando: false,
1257
- estado: 'indeterminada',
1258
- aviso: 'Não recebemos a resposta da confirmação. Verifique o resultado antes de pedir de novo.',
1259
- });
1260
- }
1261
- else if (erro.tipo === 'conflito' || erro.tipo === 'nao_encontrado') {
1262
- this.atualizarAcao(idAcao, { enviando: false, estado: 'indeterminada', aviso: erro.mensagem });
1263
- this.verificarAcao(idAcao);
1264
- }
1265
- else {
1266
- this.atualizarAcao(idAcao, { enviando: false, estado: 'pendente', aviso: erro.mensagem });
1267
- }
1268
- this.anuncios$.next(erro.mensagem);
1269
- },
1270
- });
1271
- }
1272
- cancelar(idAcao) {
1273
- const acao = this.valor.acoes[idAcao];
1274
- if (!acao || acao.enviando || acao.estado !== 'pendente')
1275
- return;
1276
- this.atualizarAcao(idAcao, { enviando: true, aviso: null });
1277
- this.api.cancelarAcao(idAcao).pipe(takeUntil(this.cancelarSessao$)).subscribe({
1278
- next: resposta => this.concluirDecisao(idAcao, resposta, null, false),
1279
- error: err => {
1280
- var _a;
1281
- const erro = this.normalizar(err);
1282
- if ((_a = erro.corpo) === null || _a === void 0 ? void 0 : _a.acao_pendente) {
1283
- this.concluirDecisao(idAcao, erro.corpo, erro, false);
1284
- }
1285
- else {
1286
- this.atualizarAcao(idAcao, { enviando: false, aviso: erro.mensagem });
1287
- }
1288
- },
1289
- });
1290
- }
1291
- /** Consulta a situação real da ação relendo o histórico da conversa. */
1292
- verificarAcao(idAcao) {
1293
- var _a;
1294
- const acao = this.valor.acoes[idAcao];
1295
- const idConversa = (_a = this.valor.conversa) === null || _a === void 0 ? void 0 : _a.id;
1296
- if (!acao || !idConversa)
1297
- return;
1298
- this.atualizarAcao(idAcao, { enviando: true });
1299
- this.api.historico(idConversa).pipe(takeUntil(this.cancelarSessao$)).subscribe({
1300
- next: r => {
1301
- const mensagens = Array.isArray(r === null || r === void 0 ? void 0 : r.data) ? r.data : [];
1302
- const atual = mensagens.map(m => adaptarAcao(m === null || m === void 0 ? void 0 : m.acao_pendente)).find(a => (a === null || a === void 0 ? void 0 : a.id) === idAcao);
1303
- if (atual) {
1304
- this.atualizarAcao(idAcao, Object.assign(Object.assign({}, atual), { enviando: false, aviso: atual.estado === 'indeterminada' ? 'Confira no sistema se a alteração foi aplicada.' : null }));
1305
- this.patch({ mensagens: this.mesclar(this.valor.mensagens, mensagens.map(adaptarMensagem).filter(Boolean)) });
1306
- }
1307
- else {
1308
- this.atualizarAcao(idAcao, {
1309
- enviando: false,
1310
- aviso: 'Não foi possível consultar o estado desta ação. Confira na tela correspondente se a alteração foi aplicada.',
1311
- });
1312
- }
1313
- },
1314
- error: err => this.atualizarAcao(idAcao, { enviando: false, aviso: this.normalizar(err).mensagem }),
1315
- });
1316
- }
1317
- limparErro() {
1318
- this.patch({ erro: null, aviso: null });
1319
- }
1320
- /* ---------------- internos ---------------- */
1321
- /** Aplica uma resposta de confirmar/cancelar (de sucesso ou de erro com corpo). */
1322
- concluirDecisao(idAcao, resposta, erro, confirmacao) {
1323
- var _a, _b;
1324
- const anterior = this.valor.acoes[idAcao];
1325
- this.aplicarResposta(resposta, !erro);
1326
- const atual = this.valor.acoes[idAcao];
1327
- // sem a ação na resposta, o resultado é desconhecido
1328
- if (!atual || atual === anterior) {
1329
- this.atualizarAcao(idAcao, {
1330
- enviando: false,
1331
- estado: confirmacao ? 'indeterminada' : anterior === null || anterior === void 0 ? void 0 : anterior.estado,
1332
- aviso: (_a = erro === null || erro === void 0 ? void 0 : erro.mensagem) !== null && _a !== void 0 ? _a : 'A API não informou o resultado. Confira se a alteração foi aplicada.',
1333
- });
1334
- }
1335
- else {
1336
- this.atualizarAcao(idAcao, { enviando: false, aviso: (_b = erro === null || erro === void 0 ? void 0 : erro.mensagem) !== null && _b !== void 0 ? _b : null });
1337
- }
1338
- const final = this.valor.acoes[idAcao];
1339
- // Sucesso só quando a API devolve a ação como executada.
1340
- if (confirmacao && (final === null || final === void 0 ? void 0 : final.estado) === 'concluida') {
1341
- this.assistant.emitir({ tipo: 'acao_concluida', idAcao, ferramenta: final.ferramenta });
1342
- this.anuncios$.next('Alteração concluída.');
1343
- }
1344
- else if ((final === null || final === void 0 ? void 0 : final.estado) === 'falha') {
1345
- this.anuncios$.next('A alteração não foi concluída.');
1346
- }
1347
- }
1348
- /** Resposta padrão (ApresentadorConversa::resposta): conversa, mensagem e ação pendente. */
1349
- aplicarResposta(resposta, emitirInstrucoes) {
1350
- var _a, _b, _c;
1351
- if (!resposta || typeof resposta !== 'object')
1352
- return;
1353
- const mensagem = adaptarMensagem(resposta.mensagem);
1354
- this.patch({
1355
- conversa: (_a = adaptarConversa(resposta.conversa)) !== null && _a !== void 0 ? _a : this.valor.conversa,
1356
- mensagens: mensagem ? this.mesclar(this.valor.mensagens, [mensagem]) : this.valor.mensagens,
1357
- acoes: this.mesclarAcoes([resposta.acao_anterior, (_b = resposta.mensagem) === null || _b === void 0 ? void 0 : _b.acao_pendente, resposta.acao_pendente]),
1358
- });
1359
- if (emitirInstrucoes && ((_c = resposta.mensagem) === null || _c === void 0 ? void 0 : _c.papel) === 'assistente') {
1360
- adaptarInstrucoes(resposta.mensagem.instrucoes_interface).forEach(e => this.assistant.emitir(e));
1361
- }
1362
- }
1363
- mesclar(atuais, novas) {
1364
- const ids = new Set(atuais.map(m => m.id));
1365
- return [...atuais, ...novas.filter(m => !ids.has(m.id))];
1366
- }
1367
- mesclarAcoes(novas) {
1368
- const acoes = Object.assign({}, this.valor.acoes);
1369
- novas.map(adaptarAcao).filter(Boolean).forEach(a => {
1370
- // uma ação já resolvida localmente não volta a "pendente" por uma resposta antiga
1371
- const anterior = acoes[a.id];
1372
- acoes[a.id] = anterior && anterior.estado !== 'pendente' && anterior.estado !== 'executando' && a.estado === 'pendente'
1373
- ? anterior
1374
- : Object.assign(Object.assign({}, a), { enviando: anterior === null || anterior === void 0 ? void 0 : anterior.enviando, aviso: anterior === null || anterior === void 0 ? void 0 : anterior.aviso });
1375
- });
1376
- return acoes;
1377
- }
1378
- atualizarAcao(id, parcial) {
1379
- const atual = this.valor.acoes[id];
1380
- if (!atual)
1381
- return;
1382
- this.patch({ acoes: Object.assign(Object.assign({}, this.valor.acoes), { [id]: Object.assign(Object.assign(Object.assign({}, atual), parcial), { id }) }) });
1383
- }
1384
- doContextoAtual(conversa) {
1385
- var _a, _b;
1386
- const sistema = this.assistant.sistemaOrigem;
1387
- const idLoja = (_a = this.assistant.contexto.idLoja) !== null && _a !== void 0 ? _a : null;
1388
- return conversa.sistemaOrigem === sistema && ((_b = conversa.idLoja) !== null && _b !== void 0 ? _b : null) === idLoja;
1389
- }
1390
- encerrarConversa(aviso) {
1391
- this.cancelarConversa$.next();
1392
- this.escopoLoja = null;
1393
- this.patch({
1394
- conversa: null,
1395
- mensagens: [],
1396
- acoes: {},
1397
- enviando: false,
1398
- carregandoConversa: false,
1399
- erro: null,
1400
- aviso,
1401
- conversas: null,
1402
- textoComFalha: null,
1403
- });
1404
- }
1405
- limparTudo() {
1406
- this.cancelarConversa$.next();
1407
- this.cancelarSessao$.next();
1408
- this.escopoLoja = null;
1409
- this.estado$.next(ESTADO_INICIAL);
1410
- }
1411
- patch(parcial) {
1412
- this.estado$.next(Object.assign(Object.assign({}, this.estado$.value), parcial));
1413
- }
1414
- novoIdLocal() {
1415
- return 'local-' + (++this.idLocal);
1416
- }
1417
- normalizar(err) {
1418
- if (err && typeof err === 'object' && typeof err.tipo === 'string' && typeof err.mensagem === 'string')
1419
- return err;
1420
- return this.erroLocal('servidor', 'Algo deu errado ao falar com a Uiny. Tente de novo.');
1421
- }
1422
- erroLocal(tipo, mensagem) {
1423
- return { tipo, status: 0, codigo: null, mensagem, correlacao: null };
1424
- }
1425
- }
1426
- UinyChatService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyChatService, deps: [{ token: UinyApiService }, { token: UinySessionService }, { token: UiAssistantService }], target: i0.ɵɵFactoryTarget.Injectable });
1427
- UinyChatService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyChatService, providedIn: 'root' });
1428
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UinyChatService, decorators: [{
1429
- type: Injectable,
1430
- args: [{ providedIn: 'root' }]
1431
- }], ctorParameters: function () { return [{ type: UinyApiService }, { type: UinySessionService }, { type: UiAssistantService }]; } });
1432
-
1433
- /*
1434
- * Subconjunto seguro de Markdown para as respostas da Uiny.
1435
- *
1436
- * O texto vira uma árvore de tokens renderizada pelo template com interpolação
1437
- * ({{ }}), sem innerHTML e sem bypass do sanitizador do Angular. HTML presente no
1438
- * texto aparece literalmente. Links só com http, https ou mailto.
1439
- *
1440
- * Suporta: parágrafos, quebras de linha, listas (- * 1.), títulos (#, ##, ###),
1441
- * blocos de código (```), `código`, **negrito**, *itálico*, [texto](url) e URLs soltas.
1442
- */
1443
- const PROTOCOLOS = ['http:', 'https:', 'mailto:'];
1444
- function urlPermitida(url) {
1445
- const bruto = (url || '').trim();
1446
- if (!bruto || bruto.length > 2000)
1447
- return null;
1448
- try {
1449
- const u = new URL(bruto);
1450
- return PROTOCOLOS.includes(u.protocol) ? u.href : null;
1451
- }
1452
- catch (_a) {
1453
- return null;
1454
- }
1455
- }
1456
- const INLINE = /(`[^`\n]+`)|(\*\*[^*\n]+\*\*)|(\[[^\]\n]{1,300}\]\([^()\s]{1,2000}\))|(\bhttps?:\/\/[^\s<>()]*[^\s<>().,;:!?'"])|(\*[^*\s][^*\n]*\*)/g;
1457
- function parseInline(texto) {
1458
- const saida = [];
1459
- let ultimo = 0;
1460
- INLINE.lastIndex = 0;
1461
- let m;
1462
- while ((m = INLINE.exec(texto)) !== null) {
1463
- if (m.index > ultimo)
1464
- saida.push({ t: 'text', v: texto.slice(ultimo, m.index) });
1465
- const token = m[0];
1466
- if (m[1]) {
1467
- saida.push({ t: 'code', v: token.slice(1, -1) });
1468
- }
1469
- else if (m[2]) {
1470
- saida.push({ t: 'strong', v: token.slice(2, -2) });
1471
- }
1472
- else if (m[3]) {
1473
- const fim = token.indexOf('](');
1474
- const rotulo = token.slice(1, fim);
1475
- const href = urlPermitida(token.slice(fim + 2, -1));
1476
- saida.push(href ? { t: 'link', v: rotulo, href } : { t: 'text', v: rotulo });
1477
- }
1478
- else if (m[4]) {
1479
- const href = urlPermitida(token);
1480
- saida.push(href ? { t: 'link', v: token, href } : { t: 'text', v: token });
1481
- }
1482
- else {
1483
- saida.push({ t: 'em', v: token.slice(1, -1) });
1484
- }
1485
- ultimo = m.index + token.length;
1486
- }
1487
- if (ultimo < texto.length)
1488
- saida.push({ t: 'text', v: texto.slice(ultimo) });
1489
- return saida;
1490
- }
1491
- function parseMarkdown(texto) {
1492
- const linhas = (texto || '').replace(/\r\n?/g, '\n').split('\n');
1493
- const blocos = [];
1494
- let paragrafo = [];
1495
- let lista = null;
1496
- const fecharParagrafo = () => {
1497
- if (paragrafo.length)
1498
- blocos.push({ t: 'p', linhas: paragrafo });
1499
- paragrafo = [];
1500
- };
1501
- const fecharLista = () => {
1502
- if (lista)
1503
- blocos.push(lista.t === 'ul' ? { t: 'ul', itens: lista.itens } : { t: 'ol', itens: lista.itens, inicio: lista.inicio });
1504
- lista = null;
1505
- };
1506
- for (let i = 0; i < linhas.length; i++) {
1507
- const linha = linhas[i];
1508
- if (/^\s*```/.test(linha)) {
1509
- fecharParagrafo();
1510
- fecharLista();
1511
- const codigo = [];
1512
- i++;
1513
- while (i < linhas.length && !/^\s*```/.test(linhas[i]))
1514
- codigo.push(linhas[i++]);
1515
- blocos.push({ t: 'code', v: codigo.join('\n') });
1516
- continue;
1517
- }
1518
- if (!linha.trim()) {
1519
- fecharParagrafo();
1520
- fecharLista();
1521
- continue;
1522
- }
1523
- const titulo = /^\s{0,3}#{1,3}\s+(.*)$/.exec(linha);
1524
- if (titulo) {
1525
- fecharParagrafo();
1526
- fecharLista();
1527
- blocos.push({ t: 'h', inlines: parseInline(titulo[1]) });
1528
- continue;
1529
- }
1530
- const ul = /^\s*[-*•]\s+(.*)$/.exec(linha);
1531
- const ol = /^\s*(\d{1,3})[.)]\s+(.*)$/.exec(linha);
1532
- if (ul || ol) {
1533
- fecharParagrafo();
1534
- const tipo = ul ? 'ul' : 'ol';
1535
- if (!lista || lista.t !== tipo) {
1536
- fecharLista();
1537
- lista = { t: tipo, itens: [], inicio: ol ? Number(ol[1]) : 1 };
1538
- }
1539
- lista.itens.push(parseInline(ul ? ul[1] : ol[2]));
1540
- continue;
1541
- }
1542
- if (lista && /^\s{2,}\S/.test(linha)) {
1543
- // continuação do último item da lista
1544
- lista.itens[lista.itens.length - 1].push({ t: 'text', v: ' ' }, ...parseInline(linha.trim()));
1545
- continue;
1546
- }
1547
- fecharLista();
1548
- paragrafo.push(parseInline(linha));
1549
- }
1550
- fecharParagrafo();
1551
- fecharLista();
1552
- return blocos;
1553
- }
1554
-
1555
- /**
1556
- * Exibe o texto da Uiny com um subconjunto de Markdown. Tudo é renderizado por
1557
- * interpolação: HTML vindo da API aparece como texto, nunca é interpretado.
1558
- */
1559
- class UiAssistantTextComponent {
1560
- constructor() {
1561
- this.texto = '';
1562
- this.blocos = [];
1563
- }
1564
- ngOnChanges() {
1565
- this.blocos = parseMarkdown(this.texto || '');
1566
- }
1567
- }
1568
- UiAssistantTextComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1569
- UiAssistantTextComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantTextComponent, selector: "ui-assistant-text", inputs: { texto: "texto" }, usesOnChanges: true, ngImport: i0, template: "<ng-template #inlines let-lista>\n <ng-container *ngFor=\"let i of lista\" [ngSwitch]=\"i.t\">\n <strong *ngSwitchCase=\"'strong'\">{{ i.v }}</strong>\n <em *ngSwitchCase=\"'em'\">{{ i.v }}</em>\n <code *ngSwitchCase=\"'code'\">{{ i.v }}</code>\n <a *ngSwitchCase=\"'link'\" [href]=\"i.href\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">{{ i.v }}</a>\n <ng-container *ngSwitchDefault>{{ i.v }}</ng-container>\n </ng-container>\n</ng-template>\n\n<ng-container *ngFor=\"let b of blocos\" [ngSwitch]=\"b.t\">\n <p *ngSwitchCase=\"'p'\">\n <ng-container *ngFor=\"let linha of $any(b).linhas; let ultima = last\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: linha }\"></ng-container><br *ngIf=\"!ultima\">\n </ng-container>\n </p>\n <p *ngSwitchCase=\"'h'\" class=\"uiny-texto__titulo\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: $any(b).inlines }\"></ng-container>\n </p>\n <ul *ngSwitchCase=\"'ul'\">\n <li *ngFor=\"let item of $any(b).itens\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: item }\"></ng-container>\n </li>\n </ul>\n <ol *ngSwitchCase=\"'ol'\" [attr.start]=\"$any(b).inicio !== 1 ? $any(b).inicio : null\">\n <li *ngFor=\"let item of $any(b).itens\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: item }\"></ng-container>\n </li>\n </ol>\n <pre *ngSwitchCase=\"'code'\"><code>{{ $any(b).v }}</code></pre>\n</ng-container>\n", styles: [":host{display:block;line-height:1.55;overflow-wrap:anywhere}p,ul,ol,pre{margin:0 0 8px}p:last-child,ul:last-child,ol:last-child,pre:last-child{margin-bottom:0}ul,ol{padding-left:20px}li+li{margin-top:3px}.uiny-texto__titulo{font-weight:700;margin-top:4px}strong{font-weight:700}code{padding:1px 5px;border-radius:5px;background:var(--uiny-fundo-suave, #f7f7fb);font-size:12.5px}pre{padding:10px 12px;border-radius:10px;background:var(--uiny-fundo-suave, #f7f7fb);overflow-x:auto;white-space:pre}pre code{padding:0;background:none}a{color:var(--uiny-primaria, #7366ff);text-decoration:underline;text-underline-offset:2px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$2.NgSwitchDefault, selector: "[ngSwitchDefault]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1570
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantTextComponent, decorators: [{
1571
- type: Component,
1572
- args: [{ selector: 'ui-assistant-text', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #inlines let-lista>\n <ng-container *ngFor=\"let i of lista\" [ngSwitch]=\"i.t\">\n <strong *ngSwitchCase=\"'strong'\">{{ i.v }}</strong>\n <em *ngSwitchCase=\"'em'\">{{ i.v }}</em>\n <code *ngSwitchCase=\"'code'\">{{ i.v }}</code>\n <a *ngSwitchCase=\"'link'\" [href]=\"i.href\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">{{ i.v }}</a>\n <ng-container *ngSwitchDefault>{{ i.v }}</ng-container>\n </ng-container>\n</ng-template>\n\n<ng-container *ngFor=\"let b of blocos\" [ngSwitch]=\"b.t\">\n <p *ngSwitchCase=\"'p'\">\n <ng-container *ngFor=\"let linha of $any(b).linhas; let ultima = last\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: linha }\"></ng-container><br *ngIf=\"!ultima\">\n </ng-container>\n </p>\n <p *ngSwitchCase=\"'h'\" class=\"uiny-texto__titulo\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: $any(b).inlines }\"></ng-container>\n </p>\n <ul *ngSwitchCase=\"'ul'\">\n <li *ngFor=\"let item of $any(b).itens\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: item }\"></ng-container>\n </li>\n </ul>\n <ol *ngSwitchCase=\"'ol'\" [attr.start]=\"$any(b).inicio !== 1 ? $any(b).inicio : null\">\n <li *ngFor=\"let item of $any(b).itens\">\n <ng-container *ngTemplateOutlet=\"inlines; context: { $implicit: item }\"></ng-container>\n </li>\n </ol>\n <pre *ngSwitchCase=\"'code'\"><code>{{ $any(b).v }}</code></pre>\n</ng-container>\n", styles: [":host{display:block;line-height:1.55;overflow-wrap:anywhere}p,ul,ol,pre{margin:0 0 8px}p:last-child,ul:last-child,ol:last-child,pre:last-child{margin-bottom:0}ul,ol{padding-left:20px}li+li{margin-top:3px}.uiny-texto__titulo{font-weight:700;margin-top:4px}strong{font-weight:700}code{padding:1px 5px;border-radius:5px;background:var(--uiny-fundo-suave, #f7f7fb);font-size:12.5px}pre{padding:10px 12px;border-radius:10px;background:var(--uiny-fundo-suave, #f7f7fb);overflow-x:auto;white-space:pre}pre code{padding:0;background:none}a{color:var(--uiny-primaria, #7366ff);text-decoration:underline;text-underline-offset:2px}\n"] }]
1573
- }], propDecorators: { texto: [{
1574
- type: Input
1575
- }] } });
1576
-
1577
- /** Distância do fim (px) até onde ainda consideramos que o usuário está "no fim". */
1578
- const PERTO_DO_FIM = 96;
1579
- class UiAssistantMessagesComponent {
1580
- constructor(cdr, zone) {
1581
- this.cdr = cdr;
1582
- this.mensagens = [];
1583
- this.acoes = {};
1584
- this.enviando = false;
1585
- this.carregando = false;
1586
- this.sugestoes = [];
1587
- this.capacidades = [];
1588
- this.temAlteracoes = false;
1589
- this.podeEnviar = true;
1590
- this.sugestao = new EventEmitter();
1591
- this.confirmar = new EventEmitter();
1592
- this.cancelar = new EventEmitter();
1593
- this.verificar = new EventEmitter();
1594
- this.novas = false;
1595
- this.pertoDoFim = true;
1596
- this.rolarPendente = true;
1597
- // o listener roda fora da zona: só reentra quando o indicador muda
1598
- zone.runOutsideAngular(() => setTimeout(() => {
1599
- var _a;
1600
- const el = (_a = this.area) === null || _a === void 0 ? void 0 : _a.nativeElement;
1601
- if (!el)
1602
- return;
1603
- const aoRolar = () => {
1604
- const perto = this.calcularPertoDoFim();
1605
- if (perto === this.pertoDoFim)
1606
- return;
1607
- this.pertoDoFim = perto;
1608
- if (perto && this.novas)
1609
- zone.run(() => {
1610
- this.novas = false;
1611
- this.cdr.markForCheck();
1612
- });
1613
- };
1614
- el.addEventListener('scroll', aoRolar, { passive: true });
1615
- this.removerScroll = () => el.removeEventListener('scroll', aoRolar);
1616
- }));
1617
- }
1618
- ngOnChanges(changes) {
1619
- var _a, _b, _c;
1620
- const mudou = changes['mensagens'] || changes['enviando'] || changes['acoes'];
1621
- if (!mudou)
1622
- return;
1623
- const mensagensMudaram = !!changes['mensagens'];
1624
- const anterior = ((_a = changes['mensagens']) === null || _a === void 0 ? void 0 : _a.previousValue) || [];
1625
- const atual = this.mensagens || [];
1626
- const conversaTrocada = mensagensMudaram && (!anterior.length || !atual.length || ((_b = anterior[0]) === null || _b === void 0 ? void 0 : _b.id) !== ((_c = atual[0]) === null || _c === void 0 ? void 0 : _c.id));
1627
- const ultima = atual[atual.length - 1];
1628
- // quem acabou de enviar quer ver a própria mensagem
1629
- const ultimaDoUsuario = mensagensMudaram && (ultima === null || ultima === void 0 ? void 0 : ultima.papel) === 'usuario' && ultima.id.startsWith('local-');
1630
- if (conversaTrocada || ultimaDoUsuario || this.pertoDoFim) {
1631
- this.rolarPendente = true;
1632
- }
1633
- else if (mensagensMudaram && atual.length > anterior.length) {
1634
- // o usuário está lendo mensagens antigas: preserva a posição
1635
- this.novas = true;
1636
- }
1637
- }
1638
- ngAfterViewChecked() {
1639
- if (!this.rolarPendente)
1640
- return;
1641
- this.rolarPendente = false;
1642
- this.irParaOFim(false);
1643
- }
1644
- ngOnDestroy() {
1645
- var _a;
1646
- (_a = this.removerScroll) === null || _a === void 0 ? void 0 : _a.call(this);
1647
- }
1648
- irParaOFim(suave = true) {
1649
- var _a, _b;
1650
- const el = (_a = this.area) === null || _a === void 0 ? void 0 : _a.nativeElement;
1651
- if (!el)
1652
- return;
1653
- const reduzir = typeof window !== 'undefined' && ((_b = window.matchMedia) === null || _b === void 0 ? void 0 : _b.call(window, '(prefers-reduced-motion: reduce)').matches);
1654
- if (suave && !reduzir && el.scrollTo) {
1655
- el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
1656
- }
1657
- else {
1658
- el.scrollTop = el.scrollHeight;
1659
- }
1660
- this.pertoDoFim = true;
1661
- if (this.novas) {
1662
- this.novas = false;
1663
- this.cdr.markForCheck();
1664
- }
1665
- }
1666
- acaoDa(mensagem) {
1667
- var _a, _b;
1668
- return mensagem.idAcao ? (_b = (_a = this.acoes) === null || _a === void 0 ? void 0 : _a[mensagem.idAcao]) !== null && _b !== void 0 ? _b : null : null;
1669
- }
1670
- /** Ações devolvidas pela API sem mensagem que as referencie: aparecem no fim. */
1671
- get acoesSemMensagem() {
1672
- const referenciadas = new Set((this.mensagens || []).map(m => m.idAcao).filter(Boolean));
1673
- return Object.values(this.acoes || {}).filter(a => !referenciadas.has(a.id));
1674
- }
1675
- trackMensagem(_, m) {
1676
- return m.id;
1677
- }
1678
- trackAcao(_, a) {
1679
- return a.id;
1680
- }
1681
- calcularPertoDoFim() {
1682
- const el = this.area.nativeElement;
1683
- return el.scrollHeight - el.scrollTop - el.clientHeight <= PERTO_DO_FIM;
1684
- }
1685
- }
1686
- UiAssistantMessagesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantMessagesComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
1687
- UiAssistantMessagesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantMessagesComponent, selector: "ui-assistant-messages", inputs: { mensagens: "mensagens", acoes: "acoes", enviando: "enviando", carregando: "carregando", sugestoes: "sugestoes", capacidades: "capacidades", temAlteracoes: "temAlteracoes", podeEnviar: "podeEnviar" }, outputs: { sugestao: "sugestao", confirmar: "confirmar", cancelar: "cancelar", verificar: "verificar" }, viewQueries: [{ propertyName: "area", first: true, predicate: ["area"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #area class=\"uiny-msgs\" role=\"log\" aria-live=\"off\" aria-label=\"Mensagens da conversa com a Uiny\" tabindex=\"0\">\r\n\r\n <div *ngIf=\"carregando\" class=\"uiny-msgs__carregando\" role=\"status\">\r\n <span class=\"uiny-spinner\" aria-hidden=\"true\"></span> Carregando a conversa\u2026\r\n </div>\r\n\r\n <div *ngIf=\"!carregando && !mensagens?.length\" class=\"uiny-boas-vindas\">\r\n <p class=\"uiny-boas-vindas__titulo\">Ol\u00E1! Eu sou a Uiny.</p>\r\n <p class=\"uiny-boas-vindas__texto\">Sou a assistente virtual da Uishop. Conte o que voc\u00EA precisa e eu ajudo por aqui.</p>\r\n <p *ngIf=\"temAlteracoes\" class=\"uiny-boas-vindas__nota\">\r\n <ui-icon icon=\"verified_user\" size=\"16\"></ui-icon>\r\n <span>Antes de alterar qualquer informa\u00E7\u00E3o, eu mostro o que vai mudar e s\u00F3 sigo com a sua confirma\u00E7\u00E3o.</span>\r\n </p>\r\n\r\n <div *ngIf=\"sugestoes?.length\" class=\"uiny-sugestoes\" aria-label=\"Sugest\u00F5es\">\r\n <button *ngFor=\"let s of sugestoes\" type=\"button\" class=\"uiny-sugestao\" [disabled]=\"!podeEnviar\" (click)=\"sugestao.emit(s)\">\r\n {{ s }}\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"capacidades?.length\" class=\"uiny-capacidades\">\r\n <p class=\"uiny-capacidades__titulo\">Posso ajudar com:</p>\r\n <ul>\r\n <li *ngFor=\"let c of capacidades\">{{ c }}</li>\r\n </ul>\r\n </div>\r\n </div>\r\n\r\n <div *ngFor=\"let m of mensagens; trackBy: trackMensagem\" class=\"uiny-msg\"\r\n [class.uiny-msg--usuario]=\"m.papel === 'usuario'\"\r\n [class.uiny-msg--assistente]=\"m.papel !== 'usuario'\"\r\n [class.uiny-msg--falha]=\"m.erro\">\r\n <span class=\"uiny-sr\">{{ m.papel === 'usuario' ? 'Voc\u00EA:' : 'Uiny:' }}</span>\r\n <div class=\"uiny-msg__balao\">\r\n <p *ngIf=\"m.papel === 'usuario'\" class=\"uiny-msg__simples\">{{ m.texto }}</p>\r\n <ui-assistant-text *ngIf=\"m.papel !== 'usuario'\" [texto]=\"m.texto\"></ui-assistant-text>\r\n </div>\r\n\r\n <ui-assistant-action-card *ngIf=\"acaoDa(m) as acao\" [acao]=\"acao\"\r\n (confirmar)=\"confirmar.emit($event)\" (cancelar)=\"cancelar.emit($event)\" (verificar)=\"verificar.emit($event)\">\r\n </ui-assistant-action-card>\r\n\r\n <!-- esclarecimento: op\u00E7\u00F5es s\u00F3 na \u00FAltima mensagem, enviadas como texto -->\r\n <div *ngIf=\"m.opcoes?.length && m === mensagens[mensagens.length - 1]\" class=\"uiny-sugestoes uiny-msg__opcoes\" aria-label=\"Op\u00E7\u00F5es de resposta\">\r\n <button *ngFor=\"let o of m.opcoes\" type=\"button\" class=\"uiny-sugestao\" [disabled]=\"!podeEnviar || enviando\" (click)=\"sugestao.emit(o)\">\r\n {{ o }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"uiny-msg__meta\">\r\n <span *ngIf=\"m.erro\" class=\"uiny-msg__nao-enviada\">\r\n <ui-icon icon=\"error\" size=\"14\"></ui-icon> N\u00E3o enviada\r\n </span>\r\n <time *ngIf=\"m.criadaEm && !m.erro\" [attr.datetime]=\"m.criadaEm.toISOString()\">{{ m.criadaEm | date:'HH:mm' }}</time>\r\n </div>\r\n </div>\r\n\r\n <ui-assistant-action-card *ngFor=\"let acao of acoesSemMensagem; trackBy: trackAcao\" class=\"uiny-msgs__acao-solta\" [acao]=\"acao\"\r\n (confirmar)=\"confirmar.emit($event)\" (cancelar)=\"cancelar.emit($event)\" (verificar)=\"verificar.emit($event)\">\r\n </ui-assistant-action-card>\r\n\r\n <div *ngIf=\"enviando\" class=\"uiny-digitando\">\r\n <span class=\"uiny-digitando__pontos\" aria-hidden=\"true\"><i></i><i></i><i></i></span>\r\n <span class=\"uiny-digitando__texto\">A Uiny est\u00E1 pensando\u2026</span>\r\n </div>\r\n</div>\r\n\r\n<button *ngIf=\"novas\" type=\"button\" class=\"uiny-novas\" (click)=\"irParaOFim()\">\r\n <ui-icon icon=\"arrow_downward\" size=\"16\"></ui-icon> Novas mensagens\r\n</button>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;min-height:0}.uiny-msgs{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:16px 14px 8px;scrollbar-width:thin}.uiny-msgs:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:-2px}.uiny-msgs__carregando{display:flex;align-items:center;gap:8px;color:var(--uiny-texto-suave, #6b6b80);font-size:13px}.uiny-spinner{width:14px;height:14px;border:2px solid var(--uiny-borda, #ececf3);border-top-color:var(--uiny-primaria, #7366ff);border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny-boas-vindas{padding:8px 2px 4px}.uiny-boas-vindas__titulo{margin:0 0 4px;font-size:18px;font-weight:700;color:var(--uiny-texto, #2b2b2b)}.uiny-boas-vindas__texto{margin:0 0 12px;color:var(--uiny-texto-suave, #6b6b80);line-height:1.5}.uiny-boas-vindas__nota{display:flex;gap:6px;margin:0 0 14px;padding:8px 10px;border-radius:10px;background:var(--uiny-fundo-suave, #f7f7fb);color:var(--uiny-texto-suave, #6b6b80);font-size:12.5px;line-height:1.45}.uiny-boas-vindas__nota ui-icon{flex:0 0 auto;color:var(--uiny-primaria, #7366ff)}.uiny-sugestoes{display:flex;flex-direction:column;align-items:flex-start;gap:6px}.uiny-sugestao{max-width:100%;padding:8px 12px;border:1px solid var(--uiny-borda, #ececf3);border-radius:12px;background:var(--uiny-fundo, #fff);color:var(--uiny-texto, #2b2b2b);font-size:13px;text-align:left;line-height:1.35;cursor:pointer;transition:border-color .15s ease,background-color .15s ease}.uiny-sugestao:hover:not(:disabled){border-color:var(--uiny-primaria, #7366ff);background:var(--uiny-aviso-fundo, #f1effe)}.uiny-sugestao:disabled{opacity:.5;cursor:not-allowed}.uiny-sugestao:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-capacidades{font-size:13px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-capacidades ul{margin:0;padding-left:18px}.uiny-capacidades li+li{margin-top:2px}.uiny-capacidades__titulo{margin:0 0 4px;font-weight:600;color:var(--uiny-texto, #2b2b2b)}.uiny-msg{display:flex;flex-direction:column;margin-top:14px}.uiny-msg:first-of-type{margin-top:0}.uiny-msg--usuario{align-items:flex-end}.uiny-msg--usuario .uiny-msg__balao{max-width:85%;padding:9px 13px;border-radius:16px 16px 4px;background:var(--uiny-primaria, #7366ff);color:#fff}.uiny-msg--assistente{align-items:stretch}.uiny-msg--assistente .uiny-msg__balao{max-width:100%;color:var(--uiny-texto, #2b2b2b)}.uiny-msg--falha .uiny-msg__balao{opacity:.65}.uiny-msg__simples{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.45}.uiny-msg__meta{margin-top:3px;font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-msg__meta time{opacity:.8}.uiny-msg__nao-enviada{display:inline-flex;align-items:center;gap:3px;color:var(--uiny-erro, #d9364f)}.uiny-msg__opcoes{margin-top:8px}.uiny-msgs__acao-solta{display:block;margin-top:14px}.uiny-digitando{display:flex;align-items:center;gap:8px;margin-top:14px;color:var(--uiny-texto-suave, #6b6b80);font-size:13px}.uiny-digitando__pontos{display:inline-flex;gap:3px}.uiny-digitando__pontos i{width:6px;height:6px;border-radius:50%;background:var(--uiny-primaria, #7366ff);animation:uiny-pulsar 1.2s ease-in-out infinite}.uiny-digitando__pontos i:nth-child(2){animation-delay:.15s}.uiny-digitando__pontos i:nth-child(3){animation-delay:.3s}.uiny-novas{position:absolute;left:50%;bottom:10px;transform:translate(-50%);display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:0;border-radius:999px;background:var(--uiny-primaria, #7366ff);color:#fff;font-size:12px;font-weight:600;box-shadow:0 6px 16px #1f1b4d33;cursor:pointer}.uiny-novas:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes uiny-girar{to{transform:rotate(360deg)}}@keyframes uiny-pulsar{0%,80%,to{opacity:.25;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.uiny-digitando__pontos i{animation:none;opacity:.6}.uiny-spinner{animation-duration:2.4s}.uiny-sugestao{transition:none}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "component", type: UiAssistantTextComponent, selector: "ui-assistant-text", inputs: ["texto"] }, { kind: "component", type: UiAssistantActionCardComponent, selector: "ui-assistant-action-card", inputs: ["acao"], outputs: ["confirmar", "cancelar", "verificar"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1688
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantMessagesComponent, decorators: [{
1689
- type: Component,
1690
- args: [{ selector: 'ui-assistant-messages', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #area class=\"uiny-msgs\" role=\"log\" aria-live=\"off\" aria-label=\"Mensagens da conversa com a Uiny\" tabindex=\"0\">\r\n\r\n <div *ngIf=\"carregando\" class=\"uiny-msgs__carregando\" role=\"status\">\r\n <span class=\"uiny-spinner\" aria-hidden=\"true\"></span> Carregando a conversa\u2026\r\n </div>\r\n\r\n <div *ngIf=\"!carregando && !mensagens?.length\" class=\"uiny-boas-vindas\">\r\n <p class=\"uiny-boas-vindas__titulo\">Ol\u00E1! Eu sou a Uiny.</p>\r\n <p class=\"uiny-boas-vindas__texto\">Sou a assistente virtual da Uishop. Conte o que voc\u00EA precisa e eu ajudo por aqui.</p>\r\n <p *ngIf=\"temAlteracoes\" class=\"uiny-boas-vindas__nota\">\r\n <ui-icon icon=\"verified_user\" size=\"16\"></ui-icon>\r\n <span>Antes de alterar qualquer informa\u00E7\u00E3o, eu mostro o que vai mudar e s\u00F3 sigo com a sua confirma\u00E7\u00E3o.</span>\r\n </p>\r\n\r\n <div *ngIf=\"sugestoes?.length\" class=\"uiny-sugestoes\" aria-label=\"Sugest\u00F5es\">\r\n <button *ngFor=\"let s of sugestoes\" type=\"button\" class=\"uiny-sugestao\" [disabled]=\"!podeEnviar\" (click)=\"sugestao.emit(s)\">\r\n {{ s }}\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"capacidades?.length\" class=\"uiny-capacidades\">\r\n <p class=\"uiny-capacidades__titulo\">Posso ajudar com:</p>\r\n <ul>\r\n <li *ngFor=\"let c of capacidades\">{{ c }}</li>\r\n </ul>\r\n </div>\r\n </div>\r\n\r\n <div *ngFor=\"let m of mensagens; trackBy: trackMensagem\" class=\"uiny-msg\"\r\n [class.uiny-msg--usuario]=\"m.papel === 'usuario'\"\r\n [class.uiny-msg--assistente]=\"m.papel !== 'usuario'\"\r\n [class.uiny-msg--falha]=\"m.erro\">\r\n <span class=\"uiny-sr\">{{ m.papel === 'usuario' ? 'Voc\u00EA:' : 'Uiny:' }}</span>\r\n <div class=\"uiny-msg__balao\">\r\n <p *ngIf=\"m.papel === 'usuario'\" class=\"uiny-msg__simples\">{{ m.texto }}</p>\r\n <ui-assistant-text *ngIf=\"m.papel !== 'usuario'\" [texto]=\"m.texto\"></ui-assistant-text>\r\n </div>\r\n\r\n <ui-assistant-action-card *ngIf=\"acaoDa(m) as acao\" [acao]=\"acao\"\r\n (confirmar)=\"confirmar.emit($event)\" (cancelar)=\"cancelar.emit($event)\" (verificar)=\"verificar.emit($event)\">\r\n </ui-assistant-action-card>\r\n\r\n <!-- esclarecimento: op\u00E7\u00F5es s\u00F3 na \u00FAltima mensagem, enviadas como texto -->\r\n <div *ngIf=\"m.opcoes?.length && m === mensagens[mensagens.length - 1]\" class=\"uiny-sugestoes uiny-msg__opcoes\" aria-label=\"Op\u00E7\u00F5es de resposta\">\r\n <button *ngFor=\"let o of m.opcoes\" type=\"button\" class=\"uiny-sugestao\" [disabled]=\"!podeEnviar || enviando\" (click)=\"sugestao.emit(o)\">\r\n {{ o }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"uiny-msg__meta\">\r\n <span *ngIf=\"m.erro\" class=\"uiny-msg__nao-enviada\">\r\n <ui-icon icon=\"error\" size=\"14\"></ui-icon> N\u00E3o enviada\r\n </span>\r\n <time *ngIf=\"m.criadaEm && !m.erro\" [attr.datetime]=\"m.criadaEm.toISOString()\">{{ m.criadaEm | date:'HH:mm' }}</time>\r\n </div>\r\n </div>\r\n\r\n <ui-assistant-action-card *ngFor=\"let acao of acoesSemMensagem; trackBy: trackAcao\" class=\"uiny-msgs__acao-solta\" [acao]=\"acao\"\r\n (confirmar)=\"confirmar.emit($event)\" (cancelar)=\"cancelar.emit($event)\" (verificar)=\"verificar.emit($event)\">\r\n </ui-assistant-action-card>\r\n\r\n <div *ngIf=\"enviando\" class=\"uiny-digitando\">\r\n <span class=\"uiny-digitando__pontos\" aria-hidden=\"true\"><i></i><i></i><i></i></span>\r\n <span class=\"uiny-digitando__texto\">A Uiny est\u00E1 pensando\u2026</span>\r\n </div>\r\n</div>\r\n\r\n<button *ngIf=\"novas\" type=\"button\" class=\"uiny-novas\" (click)=\"irParaOFim()\">\r\n <ui-icon icon=\"arrow_downward\" size=\"16\"></ui-icon> Novas mensagens\r\n</button>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;min-height:0}.uiny-msgs{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:16px 14px 8px;scrollbar-width:thin}.uiny-msgs:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:-2px}.uiny-msgs__carregando{display:flex;align-items:center;gap:8px;color:var(--uiny-texto-suave, #6b6b80);font-size:13px}.uiny-spinner{width:14px;height:14px;border:2px solid var(--uiny-borda, #ececf3);border-top-color:var(--uiny-primaria, #7366ff);border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny-boas-vindas{padding:8px 2px 4px}.uiny-boas-vindas__titulo{margin:0 0 4px;font-size:18px;font-weight:700;color:var(--uiny-texto, #2b2b2b)}.uiny-boas-vindas__texto{margin:0 0 12px;color:var(--uiny-texto-suave, #6b6b80);line-height:1.5}.uiny-boas-vindas__nota{display:flex;gap:6px;margin:0 0 14px;padding:8px 10px;border-radius:10px;background:var(--uiny-fundo-suave, #f7f7fb);color:var(--uiny-texto-suave, #6b6b80);font-size:12.5px;line-height:1.45}.uiny-boas-vindas__nota ui-icon{flex:0 0 auto;color:var(--uiny-primaria, #7366ff)}.uiny-sugestoes{display:flex;flex-direction:column;align-items:flex-start;gap:6px}.uiny-sugestao{max-width:100%;padding:8px 12px;border:1px solid var(--uiny-borda, #ececf3);border-radius:12px;background:var(--uiny-fundo, #fff);color:var(--uiny-texto, #2b2b2b);font-size:13px;text-align:left;line-height:1.35;cursor:pointer;transition:border-color .15s ease,background-color .15s ease}.uiny-sugestao:hover:not(:disabled){border-color:var(--uiny-primaria, #7366ff);background:var(--uiny-aviso-fundo, #f1effe)}.uiny-sugestao:disabled{opacity:.5;cursor:not-allowed}.uiny-sugestao:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-capacidades{font-size:13px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-capacidades ul{margin:0;padding-left:18px}.uiny-capacidades li+li{margin-top:2px}.uiny-capacidades__titulo{margin:0 0 4px;font-weight:600;color:var(--uiny-texto, #2b2b2b)}.uiny-msg{display:flex;flex-direction:column;margin-top:14px}.uiny-msg:first-of-type{margin-top:0}.uiny-msg--usuario{align-items:flex-end}.uiny-msg--usuario .uiny-msg__balao{max-width:85%;padding:9px 13px;border-radius:16px 16px 4px;background:var(--uiny-primaria, #7366ff);color:#fff}.uiny-msg--assistente{align-items:stretch}.uiny-msg--assistente .uiny-msg__balao{max-width:100%;color:var(--uiny-texto, #2b2b2b)}.uiny-msg--falha .uiny-msg__balao{opacity:.65}.uiny-msg__simples{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.45}.uiny-msg__meta{margin-top:3px;font-size:11px;color:var(--uiny-texto-suave, #6b6b80)}.uiny-msg__meta time{opacity:.8}.uiny-msg__nao-enviada{display:inline-flex;align-items:center;gap:3px;color:var(--uiny-erro, #d9364f)}.uiny-msg__opcoes{margin-top:8px}.uiny-msgs__acao-solta{display:block;margin-top:14px}.uiny-digitando{display:flex;align-items:center;gap:8px;margin-top:14px;color:var(--uiny-texto-suave, #6b6b80);font-size:13px}.uiny-digitando__pontos{display:inline-flex;gap:3px}.uiny-digitando__pontos i{width:6px;height:6px;border-radius:50%;background:var(--uiny-primaria, #7366ff);animation:uiny-pulsar 1.2s ease-in-out infinite}.uiny-digitando__pontos i:nth-child(2){animation-delay:.15s}.uiny-digitando__pontos i:nth-child(3){animation-delay:.3s}.uiny-novas{position:absolute;left:50%;bottom:10px;transform:translate(-50%);display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:0;border-radius:999px;background:var(--uiny-primaria, #7366ff);color:#fff;font-size:12px;font-weight:600;box-shadow:0 6px 16px #1f1b4d33;cursor:pointer}.uiny-novas:focus-visible{outline:2px solid var(--uiny-primaria, #7366ff);outline-offset:2px}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes uiny-girar{to{transform:rotate(360deg)}}@keyframes uiny-pulsar{0%,80%,to{opacity:.25;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.uiny-digitando__pontos i{animation:none;opacity:.6}.uiny-spinner{animation-duration:2.4s}.uiny-sugestao{transition:none}}\n"] }]
1691
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }]; }, propDecorators: { mensagens: [{
1692
- type: Input
1693
- }], acoes: [{
1694
- type: Input
1695
- }], enviando: [{
1696
- type: Input
1697
- }], carregando: [{
1698
- type: Input
1699
- }], sugestoes: [{
1700
- type: Input
1701
- }], capacidades: [{
1702
- type: Input
1703
- }], temAlteracoes: [{
1704
- type: Input
1705
- }], podeEnviar: [{
1706
- type: Input
1707
- }], sugestao: [{
1708
- type: Output
1709
- }], confirmar: [{
1710
- type: Output
1711
- }], cancelar: [{
1712
- type: Output
1713
- }], verificar: [{
1714
- type: Output
1715
- }], area: [{
1716
- type: ViewChild,
1717
- args: ['area', { static: true }]
1718
- }] } });
1719
-
1720
- class UiAssistantPanelComponent {
1721
- constructor(chat, assistant, session, cdr, config) {
1722
- this.chat = chat;
1723
- this.assistant = assistant;
1724
- this.session = session;
1725
- this.cdr = cdr;
1726
- /** Painel em largura total (celular/tablet): o botão de fechar vira "Voltar". */
1727
- this.empilhado = false;
1728
- this.online = true;
1729
- this.anuncio = '';
1730
- this.vendoHistorico = false;
1731
- this.mensagens = [];
1732
- this.acoes = {};
1733
- this.sugestoes = [];
1734
- this.capacidades = [];
1735
- this.temAlteracoes = false;
1736
- /** A imagem não carregou: usa o ícone padrão. */
1737
- this.avatarFalhou = false;
1738
- this.subs = new Subscription();
1739
- this.avatarUrl = (config === null || config === void 0 ? void 0 : config.avatarUrl) || UINY_AVATAR_PADRAO;
1740
- }
1741
- ngOnInit() {
1742
- this.subs.add(this.chat.estado.subscribe(estado => {
1743
- this.estado = estado;
1744
- this.mensagens = estado.mensagens;
1745
- this.acoes = estado.acoes;
1746
- this.cdr.markForCheck();
1747
- }));
1748
- this.subs.add(this.session.online$.subscribe(online => {
1749
- this.online = online;
1750
- this.cdr.markForCheck();
1751
- }));
1752
- this.subs.add(this.chat.anuncios.subscribe(texto => this.anunciar(texto)));
1753
- this.montarBoasVindas();
1754
- }
1755
- ngAfterViewInit() {
1756
- // espera a transição começar para não rolar a página no focus
1757
- setTimeout(() => { var _a; return (_a = this.composer) === null || _a === void 0 ? void 0 : _a.focar(); }, 60);
1758
- }
1759
- ngOnDestroy() {
1760
- clearTimeout(this.timerAnuncio);
1761
- this.subs.unsubscribe();
1762
- }
1763
- aoEscape(evento) {
1764
- if (evento.defaultPrevented)
1765
- return;
1766
- if (this.vendoHistorico) {
1767
- this.vendoHistorico = false;
1768
- return;
1769
- }
1770
- evento.preventDefault();
1771
- this.assistant.fechar();
1772
- }
1773
- get podeEnviar() {
1774
- var _a, _b;
1775
- return this.online && !((_a = this.estado) === null || _a === void 0 ? void 0 : _a.enviando) && !((_b = this.estado) === null || _b === void 0 ? void 0 : _b.carregandoConversa);
1776
- }
1777
- get motivoBloqueio() {
1778
- var _a;
1779
- if (!this.online)
1780
- return 'Sem conexão com a internet.';
1781
- if ((_a = this.estado) === null || _a === void 0 ? void 0 : _a.carregandoConversa)
1782
- return 'Carregando a conversa…';
1783
- return null;
1784
- }
1785
- fechar() {
1786
- this.assistant.fechar();
1787
- }
1788
- enviar(texto) {
1789
- var _a;
1790
- if (!this.podeEnviar)
1791
- return;
1792
- if (this.chat.enviar(texto)) {
1793
- (_a = this.composer) === null || _a === void 0 ? void 0 : _a.limpar();
1794
- this.vendoHistorico = false;
1795
- }
1796
- }
1797
- reenviar() {
1798
- var _a;
1799
- const texto = (_a = this.estado) === null || _a === void 0 ? void 0 : _a.textoComFalha;
1800
- if (texto && this.podeEnviar)
1801
- this.chat.enviar(texto);
1802
- }
1803
- usarSugestao(texto) {
1804
- this.enviar(texto);
1805
- }
1806
- novaConversa() {
1807
- if (this.chat.novaConversa()) {
1808
- this.vendoHistorico = false;
1809
- this.anunciar('Nova conversa iniciada.');
1810
- setTimeout(() => { var _a; return (_a = this.composer) === null || _a === void 0 ? void 0 : _a.focar(); });
1811
- }
1812
- }
1813
- alternarHistorico() {
1814
- this.vendoHistorico = !this.vendoHistorico;
1815
- if (this.vendoHistorico)
1816
- this.chat.listarConversas();
1817
- }
1818
- abrirConversa(id) {
1819
- this.vendoHistorico = false;
1820
- this.chat.abrirConversa(id);
1821
- setTimeout(() => { var _a; return (_a = this.composer) === null || _a === void 0 ? void 0 : _a.focar(); });
1822
- }
1823
- trackConversa(_, c) {
1824
- return c.id;
1825
- }
1826
- montarBoasVindas() {
1827
- var _a;
1828
- const sugestoes = (_a = UINY_SUGESTOES[this.assistant.sistemaOrigem]) !== null && _a !== void 0 ? _a : { textos: [], alteracoes: false };
1829
- this.sugestoes = sugestoes.textos;
1830
- this.capacidades = [];
1831
- this.temAlteracoes = sugestoes.alteracoes;
1832
- }
1833
- anunciar(texto) {
1834
- // limpa antes para leitores de tela repetirem anúncios iguais
1835
- this.anuncio = '';
1836
- this.cdr.markForCheck();
1837
- clearTimeout(this.timerAnuncio);
1838
- if (!texto)
1839
- return;
1840
- this.timerAnuncio = setTimeout(() => {
1841
- this.anuncio = texto;
1842
- this.cdr.markForCheck();
1843
- }, 50);
1844
- }
1845
- }
1846
- UiAssistantPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantPanelComponent, deps: [{ token: UinyChatService }, { token: UiAssistantService }, { token: UinySessionService }, { token: i0.ChangeDetectorRef }, { token: UINY_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1847
- UiAssistantPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantPanelComponent, selector: "ui-assistant-panel", inputs: { empilhado: "empilhado" }, host: { listeners: { "keydown.escape": "aoEscape($event)" } }, viewQueries: [{ propertyName: "composer", first: true, predicate: UiAssistantComposerComponent, descendants: true }], ngImport: i0, template: "<section class=\"uiny\" aria-labelledby=\"uiny-titulo\">\r\n <header class=\"uiny__topo\">\r\n <div class=\"uiny__marca\">\r\n <img *ngIf=\"!avatarFalhou\" class=\"uiny__avatar uiny__avatar--imagem\" [src]=\"avatarUrl\" alt=\"\"\r\n width=\"34\" height=\"34\" (error)=\"avatarFalhou = true\">\r\n <span *ngIf=\"avatarFalhou\" class=\"uiny__avatar\" aria-hidden=\"true\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" focusable=\"false\">\r\n <path d=\"M12 2.5c.5 3.9 2.6 6.9 6.4 8.2.5.2.5.9 0 1.1-3.8 1.3-5.9 4.3-6.4 8.2-.1.6-.9.6-1 0-.5-3.9-2.6-6.9-6.4-8.2-.5-.2-.5-.9 0-1.1 3.8-1.3 5.9-4.3 6.4-8.2.1-.6.9-.6 1 0Z\" fill=\"currentColor\"/>\r\n </svg>\r\n </span>\r\n <div class=\"uiny__identidade\">\r\n <h2 id=\"uiny-titulo\" class=\"uiny__nome\">Uiny</h2>\r\n <p class=\"uiny__sub\">Assistente da Uishop</p>\r\n </div>\r\n </div>\r\n\r\n <div class=\"uiny__comandos\">\r\n <button type=\"button\" class=\"uiny__icone\" [class.is-ativo]=\"vendoHistorico\"\r\n [attr.aria-pressed]=\"vendoHistorico ? 'true' : 'false'\"\r\n aria-label=\"Conversas anteriores\" title=\"Conversas anteriores\"\r\n (click)=\"alternarHistorico()\">\r\n <ui-icon icon=\"history\" size=\"20\"></ui-icon>\r\n </button>\r\n <button type=\"button\" class=\"uiny__icone\" aria-label=\"Nova conversa\" title=\"Nova conversa\"\r\n [disabled]=\"chat.ocupado\" (click)=\"novaConversa()\">\r\n <ui-icon icon=\"edit_square\" size=\"20\"></ui-icon>\r\n </button>\r\n <button type=\"button\" class=\"uiny__icone\" (click)=\"fechar()\"\r\n [attr.aria-label]=\"empilhado ? 'Voltar para a aplica\u00E7\u00E3o' : 'Fechar a Uiny'\"\r\n [title]=\"empilhado ? 'Voltar para a aplica\u00E7\u00E3o' : 'Fechar (Esc)'\">\r\n <ui-icon [icon]=\"empilhado ? 'arrow_back' : 'close'\" size=\"20\"></ui-icon>\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div *ngIf=\"!online\" class=\"uiny__faixa uiny__faixa--offline\" role=\"status\">\r\n <ui-icon icon=\"wifi_off\" size=\"18\"></ui-icon>\r\n <span>Sem conex\u00E3o com a internet. Voc\u00EA poder\u00E1 enviar mensagens quando ela voltar.</span>\r\n </div>\r\n\r\n <div *ngIf=\"estado?.aviso\" class=\"uiny__faixa uiny__faixa--info\" role=\"status\">\r\n <ui-icon icon=\"info\" size=\"18\"></ui-icon>\r\n <span>{{ estado.aviso }}</span>\r\n <button type=\"button\" class=\"uiny__faixa-fechar\" aria-label=\"Dispensar aviso\" (click)=\"chat.limparErro()\">\r\n <ui-icon icon=\"close\" size=\"16\"></ui-icon>\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"vendoHistorico; else conversaAtual\" class=\"uiny__historico\">\r\n <div class=\"uiny__historico-topo\">\r\n <h3 class=\"uiny__historico-titulo\">Conversas anteriores</h3>\r\n <button type=\"button\" class=\"uiny__link\" (click)=\"vendoHistorico = false\">Voltar \u00E0 conversa</button>\r\n </div>\r\n\r\n <div *ngIf=\"estado?.carregandoConversas\" class=\"uiny__vazio\" role=\"status\">\r\n <span class=\"uiny__spinner\" aria-hidden=\"true\"></span> Carregando conversas\u2026\r\n </div>\r\n\r\n <p *ngIf=\"!estado?.carregandoConversas && estado?.conversas?.length === 0\" class=\"uiny__vazio\">\r\n Nenhuma conversa anterior neste contexto.\r\n </p>\r\n\r\n <ul *ngIf=\"estado?.conversas?.length\" class=\"uiny__lista-conversas\">\r\n <li *ngFor=\"let c of estado.conversas; trackBy: trackConversa\">\r\n <button type=\"button\" class=\"uiny__conversa\" [class.is-atual]=\"c.id === estado.conversa?.id\"\r\n [attr.aria-current]=\"c.id === estado.conversa?.id ? 'true' : null\"\r\n [disabled]=\"chat.ocupado\" (click)=\"abrirConversa(c.id)\">\r\n <span class=\"uiny__conversa-titulo\">{{ c.titulo || 'Conversa sem t\u00EDtulo' }}</span>\r\n <span *ngIf=\"c.atualizadaEm\" class=\"uiny__conversa-data\">{{ c.atualizadaEm | date:'dd/MM/yyyy HH:mm' }}</span>\r\n </button>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <ng-template #conversaAtual>\r\n <ui-assistant-messages class=\"uiny__mensagens\"\r\n [mensagens]=\"mensagens\"\r\n [acoes]=\"acoes\"\r\n [enviando]=\"estado?.enviando\"\r\n [carregando]=\"estado?.carregandoConversa\"\r\n [sugestoes]=\"sugestoes\"\r\n [capacidades]=\"capacidades\"\r\n [temAlteracoes]=\"temAlteracoes\"\r\n [podeEnviar]=\"podeEnviar\"\r\n (sugestao)=\"usarSugestao($event)\"\r\n (confirmar)=\"chat.confirmar($event)\"\r\n (cancelar)=\"chat.cancelar($event)\"\r\n (verificar)=\"chat.verificarAcao($event)\">\r\n </ui-assistant-messages>\r\n </ng-template>\r\n\r\n <div *ngIf=\"estado?.erro\" class=\"uiny__erro\" role=\"alert\">\r\n <ui-icon icon=\"error\" size=\"18\"></ui-icon>\r\n <div class=\"uiny__erro-texto\">\r\n <span>{{ estado.erro.mensagem }}</span>\r\n <small *ngIf=\"estado.erro.correlacao\">C\u00F3digo para o suporte: {{ estado.erro.correlacao }}</small>\r\n </div>\r\n <button *ngIf=\"estado.textoComFalha && podeEnviar\" type=\"button\" class=\"uiny__link\" (click)=\"reenviar()\">\r\n Tentar de novo\r\n </button>\r\n <button type=\"button\" class=\"uiny__faixa-fechar\" aria-label=\"Dispensar erro\" (click)=\"chat.limparErro()\">\r\n <ui-icon icon=\"close\" size=\"16\"></ui-icon>\r\n </button>\r\n </div>\r\n\r\n <ui-assistant-composer class=\"uiny__composer\"\r\n [desabilitado]=\"!podeEnviar\"\r\n [enviando]=\"estado?.enviando\"\r\n [motivo]=\"motivoBloqueio\"\r\n (enviar)=\"enviar($event)\">\r\n </ui-assistant-composer>\r\n\r\n <div class=\"uiny-sr\" aria-live=\"polite\" aria-atomic=\"true\">{{ anuncio }}</div>\r\n</section>\r\n", styles: [":host{--uiny-primaria: var(--theme-default, #7366ff);--uiny-fundo: #ffffff;--uiny-fundo-suave: #f7f7fb;--uiny-borda: #ececf3;--uiny-texto: #2b2b2b;--uiny-texto-suave: #6b6b80;--uiny-erro: #d9364f;--uiny-erro-fundo: #fdeef1;--uiny-aviso-fundo: #f1effe;--uiny-offline-fundo: #fff4e5;--uiny-offline-texto: #8a5300;display:block;height:100%;color:var(--uiny-texto);font-size:14px}:host-context(.dark-only){--uiny-fundo: #262932;--uiny-fundo-suave: #1d1e26;--uiny-borda: #374558;--uiny-texto: rgba(255, 255, 255, .85);--uiny-texto-suave: #98a6ad;--uiny-erro-fundo: rgba(217, 54, 79, .16);--uiny-aviso-fundo: rgba(115, 102, 255, .16);--uiny-offline-fundo: rgba(255, 170, 0, .14);--uiny-offline-texto: #ffc76b}.uiny{display:flex;flex-direction:column;height:100%;background:var(--uiny-fundo);border-left:1px solid var(--uiny-borda);box-shadow:-4px 0 24px #1f1b4d0f}.uiny__topo{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 12px 14px 16px;border-bottom:1px solid var(--uiny-borda)}.uiny__marca{display:flex;align-items:center;gap:10px;min-width:0}.uiny__avatar{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:11px;color:#fff;background:linear-gradient(135deg,var(--uiny-primaria) 0%,#a26cf8 100%)}.uiny__avatar--imagem{object-fit:cover;background:var(--uiny-fundo-suave)}.uiny__identidade{min-width:0}.uiny__nome{margin:0;font-size:16px;font-weight:700;line-height:1.2;color:var(--uiny-texto)}.uiny__sub{margin:0;font-size:12px;line-height:1.3;color:var(--uiny-texto-suave)}.uiny__comandos{display:flex;align-items:center;gap:2px}.uiny__icone{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border:0;border-radius:9px;background:transparent;color:var(--uiny-texto-suave);cursor:pointer}.uiny__icone:hover:not(:disabled),.uiny__icone.is-ativo{background:var(--uiny-fundo-suave);color:var(--uiny-primaria)}.uiny__icone:disabled{opacity:.4;cursor:not-allowed}button:focus-visible{outline:2px solid var(--uiny-primaria);outline-offset:2px}.uiny__faixa,.uiny__erro{flex:0 0 auto;display:flex;align-items:flex-start;gap:8px;margin:10px 12px 0;padding:9px 10px;border-radius:10px;font-size:13px;line-height:1.4}.uiny__faixa>ui-icon,.uiny__erro>ui-icon{flex:0 0 auto;margin-top:1px}.uiny__faixa>span,.uiny__faixa .uiny__erro-texto,.uiny__erro>span,.uiny__erro .uiny__erro-texto{flex:1 1 auto;min-width:0}.uiny__faixa--offline{background:var(--uiny-offline-fundo);color:var(--uiny-offline-texto)}.uiny__faixa--info{background:var(--uiny-aviso-fundo);color:var(--uiny-texto)}.uiny__erro{margin:0 12px 8px;background:var(--uiny-erro-fundo);color:var(--uiny-erro);align-items:center}.uiny__erro .uiny__erro-texto{display:flex;flex-direction:column;gap:2px}.uiny__erro .uiny__erro-texto small{opacity:.8;font-size:11px}.uiny__faixa-fechar{flex:0 0 auto;display:inline-flex;padding:2px;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;opacity:.7}.uiny__faixa-fechar:hover{opacity:1}.uiny__link{flex:0 0 auto;padding:0;border:0;background:none;color:var(--uiny-primaria);font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}.uiny__link:hover{text-decoration:underline}.uiny__mensagens{flex:1 1 auto;min-height:0}.uiny__historico{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 12px}.uiny__historico-topo{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;padding:0 4px}.uiny__historico-titulo{margin:0;font-size:13px;font-weight:700;color:var(--uiny-texto)}.uiny__lista-conversas{margin:0;padding:0;list-style:none}.uiny__conversa{display:flex;flex-direction:column;gap:2px;width:100%;padding:10px 12px;border:1px solid transparent;border-radius:10px;background:transparent;color:var(--uiny-texto);text-align:left;cursor:pointer}.uiny__conversa:hover:not(:disabled){background:var(--uiny-fundo-suave)}.uiny__conversa.is-atual{border-color:var(--uiny-borda);background:var(--uiny-fundo-suave)}.uiny__conversa:disabled{opacity:.5;cursor:not-allowed}.uiny__conversa-titulo{overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.uiny__conversa-data{font-size:12px;color:var(--uiny-texto-suave)}.uiny__vazio{display:flex;align-items:center;gap:8px;margin:18px 4px;color:var(--uiny-texto-suave);font-size:13px}.uiny__spinner{width:14px;height:14px;border:2px solid var(--uiny-borda);border-top-color:var(--uiny-primaria);border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny__composer{flex:0 0 auto}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny__spinner{animation-duration:2.4s}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.UiIconComponent, selector: "ui-icon", inputs: ["icon", "size", "class"] }, { kind: "component", type: UiAssistantMessagesComponent, selector: "ui-assistant-messages", inputs: ["mensagens", "acoes", "enviando", "carregando", "sugestoes", "capacidades", "temAlteracoes", "podeEnviar"], outputs: ["sugestao", "confirmar", "cancelar", "verificar"] }, { kind: "component", type: UiAssistantComposerComponent, selector: "ui-assistant-composer", inputs: ["desabilitado", "enviando", "motivo"], outputs: ["enviar"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1848
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantPanelComponent, decorators: [{
1849
- type: Component,
1850
- args: [{ selector: 'ui-assistant-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"uiny\" aria-labelledby=\"uiny-titulo\">\r\n <header class=\"uiny__topo\">\r\n <div class=\"uiny__marca\">\r\n <img *ngIf=\"!avatarFalhou\" class=\"uiny__avatar uiny__avatar--imagem\" [src]=\"avatarUrl\" alt=\"\"\r\n width=\"34\" height=\"34\" (error)=\"avatarFalhou = true\">\r\n <span *ngIf=\"avatarFalhou\" class=\"uiny__avatar\" aria-hidden=\"true\">\r\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" focusable=\"false\">\r\n <path d=\"M12 2.5c.5 3.9 2.6 6.9 6.4 8.2.5.2.5.9 0 1.1-3.8 1.3-5.9 4.3-6.4 8.2-.1.6-.9.6-1 0-.5-3.9-2.6-6.9-6.4-8.2-.5-.2-.5-.9 0-1.1 3.8-1.3 5.9-4.3 6.4-8.2.1-.6.9-.6 1 0Z\" fill=\"currentColor\"/>\r\n </svg>\r\n </span>\r\n <div class=\"uiny__identidade\">\r\n <h2 id=\"uiny-titulo\" class=\"uiny__nome\">Uiny</h2>\r\n <p class=\"uiny__sub\">Assistente da Uishop</p>\r\n </div>\r\n </div>\r\n\r\n <div class=\"uiny__comandos\">\r\n <button type=\"button\" class=\"uiny__icone\" [class.is-ativo]=\"vendoHistorico\"\r\n [attr.aria-pressed]=\"vendoHistorico ? 'true' : 'false'\"\r\n aria-label=\"Conversas anteriores\" title=\"Conversas anteriores\"\r\n (click)=\"alternarHistorico()\">\r\n <ui-icon icon=\"history\" size=\"20\"></ui-icon>\r\n </button>\r\n <button type=\"button\" class=\"uiny__icone\" aria-label=\"Nova conversa\" title=\"Nova conversa\"\r\n [disabled]=\"chat.ocupado\" (click)=\"novaConversa()\">\r\n <ui-icon icon=\"edit_square\" size=\"20\"></ui-icon>\r\n </button>\r\n <button type=\"button\" class=\"uiny__icone\" (click)=\"fechar()\"\r\n [attr.aria-label]=\"empilhado ? 'Voltar para a aplica\u00E7\u00E3o' : 'Fechar a Uiny'\"\r\n [title]=\"empilhado ? 'Voltar para a aplica\u00E7\u00E3o' : 'Fechar (Esc)'\">\r\n <ui-icon [icon]=\"empilhado ? 'arrow_back' : 'close'\" size=\"20\"></ui-icon>\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <div *ngIf=\"!online\" class=\"uiny__faixa uiny__faixa--offline\" role=\"status\">\r\n <ui-icon icon=\"wifi_off\" size=\"18\"></ui-icon>\r\n <span>Sem conex\u00E3o com a internet. Voc\u00EA poder\u00E1 enviar mensagens quando ela voltar.</span>\r\n </div>\r\n\r\n <div *ngIf=\"estado?.aviso\" class=\"uiny__faixa uiny__faixa--info\" role=\"status\">\r\n <ui-icon icon=\"info\" size=\"18\"></ui-icon>\r\n <span>{{ estado.aviso }}</span>\r\n <button type=\"button\" class=\"uiny__faixa-fechar\" aria-label=\"Dispensar aviso\" (click)=\"chat.limparErro()\">\r\n <ui-icon icon=\"close\" size=\"16\"></ui-icon>\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"vendoHistorico; else conversaAtual\" class=\"uiny__historico\">\r\n <div class=\"uiny__historico-topo\">\r\n <h3 class=\"uiny__historico-titulo\">Conversas anteriores</h3>\r\n <button type=\"button\" class=\"uiny__link\" (click)=\"vendoHistorico = false\">Voltar \u00E0 conversa</button>\r\n </div>\r\n\r\n <div *ngIf=\"estado?.carregandoConversas\" class=\"uiny__vazio\" role=\"status\">\r\n <span class=\"uiny__spinner\" aria-hidden=\"true\"></span> Carregando conversas\u2026\r\n </div>\r\n\r\n <p *ngIf=\"!estado?.carregandoConversas && estado?.conversas?.length === 0\" class=\"uiny__vazio\">\r\n Nenhuma conversa anterior neste contexto.\r\n </p>\r\n\r\n <ul *ngIf=\"estado?.conversas?.length\" class=\"uiny__lista-conversas\">\r\n <li *ngFor=\"let c of estado.conversas; trackBy: trackConversa\">\r\n <button type=\"button\" class=\"uiny__conversa\" [class.is-atual]=\"c.id === estado.conversa?.id\"\r\n [attr.aria-current]=\"c.id === estado.conversa?.id ? 'true' : null\"\r\n [disabled]=\"chat.ocupado\" (click)=\"abrirConversa(c.id)\">\r\n <span class=\"uiny__conversa-titulo\">{{ c.titulo || 'Conversa sem t\u00EDtulo' }}</span>\r\n <span *ngIf=\"c.atualizadaEm\" class=\"uiny__conversa-data\">{{ c.atualizadaEm | date:'dd/MM/yyyy HH:mm' }}</span>\r\n </button>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <ng-template #conversaAtual>\r\n <ui-assistant-messages class=\"uiny__mensagens\"\r\n [mensagens]=\"mensagens\"\r\n [acoes]=\"acoes\"\r\n [enviando]=\"estado?.enviando\"\r\n [carregando]=\"estado?.carregandoConversa\"\r\n [sugestoes]=\"sugestoes\"\r\n [capacidades]=\"capacidades\"\r\n [temAlteracoes]=\"temAlteracoes\"\r\n [podeEnviar]=\"podeEnviar\"\r\n (sugestao)=\"usarSugestao($event)\"\r\n (confirmar)=\"chat.confirmar($event)\"\r\n (cancelar)=\"chat.cancelar($event)\"\r\n (verificar)=\"chat.verificarAcao($event)\">\r\n </ui-assistant-messages>\r\n </ng-template>\r\n\r\n <div *ngIf=\"estado?.erro\" class=\"uiny__erro\" role=\"alert\">\r\n <ui-icon icon=\"error\" size=\"18\"></ui-icon>\r\n <div class=\"uiny__erro-texto\">\r\n <span>{{ estado.erro.mensagem }}</span>\r\n <small *ngIf=\"estado.erro.correlacao\">C\u00F3digo para o suporte: {{ estado.erro.correlacao }}</small>\r\n </div>\r\n <button *ngIf=\"estado.textoComFalha && podeEnviar\" type=\"button\" class=\"uiny__link\" (click)=\"reenviar()\">\r\n Tentar de novo\r\n </button>\r\n <button type=\"button\" class=\"uiny__faixa-fechar\" aria-label=\"Dispensar erro\" (click)=\"chat.limparErro()\">\r\n <ui-icon icon=\"close\" size=\"16\"></ui-icon>\r\n </button>\r\n </div>\r\n\r\n <ui-assistant-composer class=\"uiny__composer\"\r\n [desabilitado]=\"!podeEnviar\"\r\n [enviando]=\"estado?.enviando\"\r\n [motivo]=\"motivoBloqueio\"\r\n (enviar)=\"enviar($event)\">\r\n </ui-assistant-composer>\r\n\r\n <div class=\"uiny-sr\" aria-live=\"polite\" aria-atomic=\"true\">{{ anuncio }}</div>\r\n</section>\r\n", styles: [":host{--uiny-primaria: var(--theme-default, #7366ff);--uiny-fundo: #ffffff;--uiny-fundo-suave: #f7f7fb;--uiny-borda: #ececf3;--uiny-texto: #2b2b2b;--uiny-texto-suave: #6b6b80;--uiny-erro: #d9364f;--uiny-erro-fundo: #fdeef1;--uiny-aviso-fundo: #f1effe;--uiny-offline-fundo: #fff4e5;--uiny-offline-texto: #8a5300;display:block;height:100%;color:var(--uiny-texto);font-size:14px}:host-context(.dark-only){--uiny-fundo: #262932;--uiny-fundo-suave: #1d1e26;--uiny-borda: #374558;--uiny-texto: rgba(255, 255, 255, .85);--uiny-texto-suave: #98a6ad;--uiny-erro-fundo: rgba(217, 54, 79, .16);--uiny-aviso-fundo: rgba(115, 102, 255, .16);--uiny-offline-fundo: rgba(255, 170, 0, .14);--uiny-offline-texto: #ffc76b}.uiny{display:flex;flex-direction:column;height:100%;background:var(--uiny-fundo);border-left:1px solid var(--uiny-borda);box-shadow:-4px 0 24px #1f1b4d0f}.uiny__topo{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:14px 12px 14px 16px;border-bottom:1px solid var(--uiny-borda)}.uiny__marca{display:flex;align-items:center;gap:10px;min-width:0}.uiny__avatar{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:11px;color:#fff;background:linear-gradient(135deg,var(--uiny-primaria) 0%,#a26cf8 100%)}.uiny__avatar--imagem{object-fit:cover;background:var(--uiny-fundo-suave)}.uiny__identidade{min-width:0}.uiny__nome{margin:0;font-size:16px;font-weight:700;line-height:1.2;color:var(--uiny-texto)}.uiny__sub{margin:0;font-size:12px;line-height:1.3;color:var(--uiny-texto-suave)}.uiny__comandos{display:flex;align-items:center;gap:2px}.uiny__icone{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border:0;border-radius:9px;background:transparent;color:var(--uiny-texto-suave);cursor:pointer}.uiny__icone:hover:not(:disabled),.uiny__icone.is-ativo{background:var(--uiny-fundo-suave);color:var(--uiny-primaria)}.uiny__icone:disabled{opacity:.4;cursor:not-allowed}button:focus-visible{outline:2px solid var(--uiny-primaria);outline-offset:2px}.uiny__faixa,.uiny__erro{flex:0 0 auto;display:flex;align-items:flex-start;gap:8px;margin:10px 12px 0;padding:9px 10px;border-radius:10px;font-size:13px;line-height:1.4}.uiny__faixa>ui-icon,.uiny__erro>ui-icon{flex:0 0 auto;margin-top:1px}.uiny__faixa>span,.uiny__faixa .uiny__erro-texto,.uiny__erro>span,.uiny__erro .uiny__erro-texto{flex:1 1 auto;min-width:0}.uiny__faixa--offline{background:var(--uiny-offline-fundo);color:var(--uiny-offline-texto)}.uiny__faixa--info{background:var(--uiny-aviso-fundo);color:var(--uiny-texto)}.uiny__erro{margin:0 12px 8px;background:var(--uiny-erro-fundo);color:var(--uiny-erro);align-items:center}.uiny__erro .uiny__erro-texto{display:flex;flex-direction:column;gap:2px}.uiny__erro .uiny__erro-texto small{opacity:.8;font-size:11px}.uiny__faixa-fechar{flex:0 0 auto;display:inline-flex;padding:2px;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;opacity:.7}.uiny__faixa-fechar:hover{opacity:1}.uiny__link{flex:0 0 auto;padding:0;border:0;background:none;color:var(--uiny-primaria);font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}.uiny__link:hover{text-decoration:underline}.uiny__mensagens{flex:1 1 auto;min-height:0}.uiny__historico{flex:1 1 auto;min-height:0;overflow-y:auto;padding:14px 12px}.uiny__historico-topo{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;padding:0 4px}.uiny__historico-titulo{margin:0;font-size:13px;font-weight:700;color:var(--uiny-texto)}.uiny__lista-conversas{margin:0;padding:0;list-style:none}.uiny__conversa{display:flex;flex-direction:column;gap:2px;width:100%;padding:10px 12px;border:1px solid transparent;border-radius:10px;background:transparent;color:var(--uiny-texto);text-align:left;cursor:pointer}.uiny__conversa:hover:not(:disabled){background:var(--uiny-fundo-suave)}.uiny__conversa.is-atual{border-color:var(--uiny-borda);background:var(--uiny-fundo-suave)}.uiny__conversa:disabled{opacity:.5;cursor:not-allowed}.uiny__conversa-titulo{overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.uiny__conversa-data{font-size:12px;color:var(--uiny-texto-suave)}.uiny__vazio{display:flex;align-items:center;gap:8px;margin:18px 4px;color:var(--uiny-texto-suave);font-size:13px}.uiny__spinner{width:14px;height:14px;border:2px solid var(--uiny-borda);border-top-color:var(--uiny-primaria);border-radius:50%;animation:uiny-girar .8s linear infinite}.uiny__composer{flex:0 0 auto}.uiny-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@keyframes uiny-girar{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.uiny__spinner{animation-duration:2.4s}}\n"] }]
1851
- }], ctorParameters: function () {
1852
- return [{ type: UinyChatService }, { type: UiAssistantService }, { type: UinySessionService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
1853
- type: Optional
1854
- }, {
1855
- type: Inject,
1856
- args: [UINY_CONFIG]
1857
- }] }];
1858
- }, propDecorators: { empilhado: [{
1859
- type: Input
1860
- }], composer: [{
1861
- type: ViewChild,
1862
- args: [UiAssistantComposerComponent]
1863
- }], aoEscape: [{
1864
- type: HostListener,
1865
- args: ['keydown.escape', ['$event']]
1866
- }] } });
1867
-
1868
- const DURACAO_TRANSICAO_MS = 240;
1869
- /**
1870
- * Contêiner que hospeda a Uiny ao lado da aplicação.
1871
- *
1872
- * Envolve TODO o layout da aplicação (inclusive o header e a sidebar fixos):
1873
- *
1874
- * <ui-assistant-layout (assistantEvent)="...">
1875
- * <div class="page-wrapper">...</div>
1876
- * </ui-assistant-layout>
1877
- *
1878
- * Fechado, não altera nada: a página rola normalmente pela janela.
1879
- * Aberto no desktop, vira uma linha flex [aplicação | painel]: a coluna da aplicação passa
1880
- * a ser o bloco de contenção dos elementos position:fixed (contain: layout) e ganha rolagem
1881
- * própria. Assim o header e a sidebar fixos ocupam a coluna da esquerda, sem serem cobertos.
1882
- * Em telas estreitas o painel ocupa a largura toda e a aplicação fica recolhida até voltar.
1883
- */
1884
- class UiAssistantLayoutComponent {
1885
- constructor(assistant, cdr, zone, config, router) {
1886
- this.assistant = assistant;
1887
- this.cdr = cdr;
1888
- this.zone = zone;
1889
- this.router = router;
1890
- /** Ações concluídas e instruções de interface (tema atualizado, atualizar listagem, abrir prévia). */
1891
- this.assistantEvent = new EventEmitter();
1892
- /** Painel montado e layout em modo contido (inclui a animação de fechamento). */
1893
- this.contido = false;
1894
- /** Painel com a largura final (classe que dispara a transição). */
1895
- this.expandido = false;
1896
- /** Tela estreita: painel em largura total, aplicação recolhida. */
1897
- this.empilhado = false;
1898
- this.disponivel = false;
1899
- this.subs = new Subscription();
1900
- this.midia = null;
1901
- this.scrollJanela = 0;
1902
- this.largura = larguraPainel(config);
1903
- const ladoALado = (config === null || config === void 0 ? void 0 : config.larguraMinimaLadoALado) || UINY_LADO_A_LADO_PADRAO;
1904
- if (typeof window !== 'undefined' && window.matchMedia) {
1905
- this.midia = window.matchMedia(`(max-width: ${ladoALado - 0.02}px)`);
1906
- this.empilhado = this.midia.matches;
1907
- }
1908
- }
1909
- ngOnInit() {
1910
- this.removerHost = this.assistant.registrarHost();
1911
- this.subs.add(this.assistant.disponivel$.subscribe(d => {
1912
- this.disponivel = d;
1913
- this.cdr.markForCheck();
1914
- }));
1915
- this.subs.add(this.assistant.aberto$.subscribe(aberto => aberto ? this.abrir() : this.fechar()));
1916
- this.subs.add(this.assistant.eventos$.subscribe(e => this.assistantEvent.emit(e)));
1917
- // Com o layout contido a janela não rola: replica o scroll ao topo da navegação.
1918
- if (this.router) {
1919
- this.subs.add(this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => {
1920
- if (this.contido && this.rolagem)
1921
- this.rolagem.nativeElement.scrollTop = 0;
1922
- }));
1923
- }
1924
- if (this.midia) {
1925
- const aoMudar = (e) => this.zone.run(() => {
1926
- this.empilhado = e.matches;
1927
- this.cdr.markForCheck();
1928
- });
1929
- this.midia.addEventListener ? this.midia.addEventListener('change', aoMudar) : this.midia.addListener(aoMudar);
1930
- this.subs.add(() => this.midia.removeEventListener
1931
- ? this.midia.removeEventListener('change', aoMudar)
1932
- : this.midia.removeListener(aoMudar));
1933
- }
1934
- }
1935
- ngOnDestroy() {
1936
- var _a;
1937
- clearTimeout(this.timerFechamento);
1938
- this.subs.unsubscribe();
1939
- (_a = this.removerHost) === null || _a === void 0 ? void 0 : _a.call(this);
1940
- if (this.contido)
1941
- this.restaurarJanela();
1942
- }
1943
- abrir() {
1944
- clearTimeout(this.timerFechamento);
1945
- if (!this.contido) {
1946
- this.scrollJanela = window.scrollY || 0;
1947
- this.contido = true;
1948
- this.cdr.detectChanges();
1949
- // a rolagem passa da janela para a coluna da aplicação, na mesma posição
1950
- this.rolagem.nativeElement.scrollTop = this.scrollJanela;
1951
- }
1952
- // próximo frame: a largura sai de 0 e a transição acontece
1953
- requestAnimationFrame(() => {
1954
- this.expandido = true;
1955
- this.cdr.detectChanges();
1956
- });
1957
- }
1958
- fechar() {
1959
- if (!this.contido)
1960
- return;
1961
- this.expandido = false;
1962
- this.cdr.detectChanges();
1963
- clearTimeout(this.timerFechamento);
1964
- this.timerFechamento = setTimeout(() => {
1965
- this.restaurarJanela();
1966
- this.cdr.detectChanges();
1967
- }, this.semAnimacao() ? 0 : DURACAO_TRANSICAO_MS);
1968
- }
1969
- restaurarJanela() {
1970
- var _a, _b;
1971
- const topo = this.empilhado ? this.scrollJanela : ((_b = (_a = this.rolagem) === null || _a === void 0 ? void 0 : _a.nativeElement.scrollTop) !== null && _b !== void 0 ? _b : 0);
1972
- this.contido = false;
1973
- this.cdr.detectChanges();
1974
- window.scrollTo(0, topo);
1975
- }
1976
- semAnimacao() {
1977
- return typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
1978
- }
1979
- }
1980
- UiAssistantLayoutComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantLayoutComponent, deps: [{ token: UiAssistantService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: UINY_CONFIG, optional: true }, { token: i2$1.Router, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1981
- UiAssistantLayoutComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: UiAssistantLayoutComponent, selector: "ui-assistant-layout", outputs: { assistantEvent: "assistantEvent" }, viewQueries: [{ propertyName: "rolagem", first: true, predicate: ["rolagem"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"uiny-layout\"\n [class.uiny-layout--contido]=\"contido\"\n [class.uiny-layout--expandido]=\"expandido\"\n [class.uiny-layout--empilhado]=\"empilhado\"\n [style.--uiny-largura]=\"largura + 'px'\">\n\n <div class=\"uiny-layout__app\"\n [attr.inert]=\"contido && expandido && empilhado ? '' : null\"\n [attr.aria-hidden]=\"contido && expandido && empilhado ? 'true' : null\">\n <div #rolagem class=\"uiny-layout__rolagem\">\n <ng-content></ng-content>\n </div>\n </div>\n\n <aside *ngIf=\"disponivel && contido\" id=\"uiny-painel\" class=\"uiny-layout__painel\"\n aria-label=\"Uiny, assistente da Uishop\"\n [attr.aria-hidden]=\"expandido ? null : 'true'\"\n [attr.inert]=\"expandido ? null : ''\">\n <div class=\"uiny-layout__painel-conteudo\">\n <ui-assistant-panel [empilhado]=\"empilhado\"></ui-assistant-panel>\n </div>\n </aside>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.uiny-layout,.uiny-layout__app,.uiny-layout__rolagem{display:block}.uiny-layout--contido{display:flex;width:100%;height:100vh;height:100dvh;overflow:hidden}.uiny-layout--contido .uiny-layout__app{flex:1 1 0;min-width:0;height:100%;position:relative;contain:layout paint;overflow:hidden}.uiny-layout--contido .uiny-layout__rolagem{height:100%;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}.uiny-layout__painel{flex:0 0 auto;width:0;height:100%;overflow:hidden;position:relative;z-index:2;transition:width .24s cubic-bezier(.2,.8,.2,1)}.uiny-layout__painel-conteudo{width:var(--uiny-largura, 380px);height:100%}.uiny-layout--expandido .uiny-layout__painel{width:var(--uiny-largura, 380px)}.uiny-layout--empilhado .uiny-layout__painel,.uiny-layout--empilhado .uiny-layout__painel-conteudo{width:100%}.uiny-layout--empilhado .uiny-layout__painel{transition:opacity .18s ease;opacity:0}.uiny-layout--empilhado.uiny-layout--expandido .uiny-layout__painel{width:100%;opacity:1}.uiny-layout--empilhado.uiny-layout--expandido .uiny-layout__app{display:none}@media (prefers-reduced-motion: reduce){.uiny-layout__painel,.uiny-layout--empilhado .uiny-layout__painel{transition:none}}@media print{.uiny-layout__painel{display:none}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: UiAssistantPanelComponent, selector: "ui-assistant-panel", inputs: ["empilhado"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1982
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantLayoutComponent, decorators: [{
1983
- type: Component,
1984
- args: [{ selector: 'ui-assistant-layout', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"uiny-layout\"\n [class.uiny-layout--contido]=\"contido\"\n [class.uiny-layout--expandido]=\"expandido\"\n [class.uiny-layout--empilhado]=\"empilhado\"\n [style.--uiny-largura]=\"largura + 'px'\">\n\n <div class=\"uiny-layout__app\"\n [attr.inert]=\"contido && expandido && empilhado ? '' : null\"\n [attr.aria-hidden]=\"contido && expandido && empilhado ? 'true' : null\">\n <div #rolagem class=\"uiny-layout__rolagem\">\n <ng-content></ng-content>\n </div>\n </div>\n\n <aside *ngIf=\"disponivel && contido\" id=\"uiny-painel\" class=\"uiny-layout__painel\"\n aria-label=\"Uiny, assistente da Uishop\"\n [attr.aria-hidden]=\"expandido ? null : 'true'\"\n [attr.inert]=\"expandido ? null : ''\">\n <div class=\"uiny-layout__painel-conteudo\">\n <ui-assistant-panel [empilhado]=\"empilhado\"></ui-assistant-panel>\n </div>\n </aside>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.uiny-layout,.uiny-layout__app,.uiny-layout__rolagem{display:block}.uiny-layout--contido{display:flex;width:100%;height:100vh;height:100dvh;overflow:hidden}.uiny-layout--contido .uiny-layout__app{flex:1 1 0;min-width:0;height:100%;position:relative;contain:layout paint;overflow:hidden}.uiny-layout--contido .uiny-layout__rolagem{height:100%;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}.uiny-layout__painel{flex:0 0 auto;width:0;height:100%;overflow:hidden;position:relative;z-index:2;transition:width .24s cubic-bezier(.2,.8,.2,1)}.uiny-layout__painel-conteudo{width:var(--uiny-largura, 380px);height:100%}.uiny-layout--expandido .uiny-layout__painel{width:var(--uiny-largura, 380px)}.uiny-layout--empilhado .uiny-layout__painel,.uiny-layout--empilhado .uiny-layout__painel-conteudo{width:100%}.uiny-layout--empilhado .uiny-layout__painel{transition:opacity .18s ease;opacity:0}.uiny-layout--empilhado.uiny-layout--expandido .uiny-layout__painel{width:100%;opacity:1}.uiny-layout--empilhado.uiny-layout--expandido .uiny-layout__app{display:none}@media (prefers-reduced-motion: reduce){.uiny-layout__painel,.uiny-layout--empilhado .uiny-layout__painel{transition:none}}@media print{.uiny-layout__painel{display:none}}\n"] }]
1985
- }], ctorParameters: function () {
1986
- return [{ type: UiAssistantService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
1987
- type: Optional
1988
- }, {
1989
- type: Inject,
1990
- args: [UINY_CONFIG]
1991
- }] }, { type: i2$1.Router, decorators: [{
1992
- type: Optional
1993
- }] }];
1994
- }, propDecorators: { assistantEvent: [{
1995
- type: Output
1996
- }], rolagem: [{
1997
- type: ViewChild,
1998
- args: ['rolagem', { static: true }]
1999
- }] } });
2000
-
2001
- const COMPONENTES = [
2002
- UiAssistantButtonComponent,
2003
- UiAssistantLayoutComponent,
2004
- UiAssistantPanelComponent,
2005
- UiAssistantMessagesComponent,
2006
- UiAssistantTextComponent,
2007
- UiAssistantComposerComponent,
2008
- UiAssistantActionCardComponent,
2009
- ];
2010
- class UiAssistantModule {
2011
- /** Importe uma vez no AppModule do consumidor. */
2012
- static forRoot(config) {
2013
- return {
2014
- ngModule: UiAssistantModule,
2015
- providers: [{ provide: UINY_CONFIG, useValue: config }]
2016
- };
2017
- }
2018
- }
2019
- UiAssistantModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2020
- UiAssistantModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantModule, declarations: [UiAssistantButtonComponent,
2021
- UiAssistantLayoutComponent,
2022
- UiAssistantPanelComponent,
2023
- UiAssistantMessagesComponent,
2024
- UiAssistantTextComponent,
2025
- UiAssistantComposerComponent,
2026
- UiAssistantActionCardComponent], imports: [CommonModule,
2027
- UiIconModule], exports: [UiAssistantButtonComponent,
2028
- UiAssistantLayoutComponent,
2029
- UiAssistantPanelComponent,
2030
- UiAssistantMessagesComponent,
2031
- UiAssistantTextComponent,
2032
- UiAssistantComposerComponent,
2033
- UiAssistantActionCardComponent] });
2034
- UiAssistantModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantModule, imports: [CommonModule,
2035
- UiIconModule] });
2036
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiAssistantModule, decorators: [{
2037
- type: NgModule,
2038
- args: [{
2039
- declarations: COMPONENTES,
2040
- imports: [
2041
- CommonModule,
2042
- UiIconModule
2043
- ],
2044
- exports: COMPONENTES
2045
- }]
2046
- }] });
2047
-
2048
269
  class UiHeaderModule {
2049
270
  }
2050
271
  UiHeaderModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -2053,15 +274,12 @@ UiHeaderModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
2053
274
  ReactiveFormsModule,
2054
275
  UiIconModule,
2055
276
  UiUserTopModule,
2056
- TooltipModule,
2057
- UiAssistantModule], exports: [UiHeaderComponent,
2058
- UiAssistantModule] });
277
+ TooltipModule], exports: [UiHeaderComponent] });
2059
278
  UiHeaderModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderModule, imports: [CommonModule,
2060
279
  ReactiveFormsModule,
2061
280
  UiIconModule,
2062
281
  UiUserTopModule,
2063
- TooltipModule,
2064
- UiAssistantModule, UiAssistantModule] });
282
+ TooltipModule] });
2065
283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: UiHeaderModule, decorators: [{
2066
284
  type: NgModule,
2067
285
  args: [{
@@ -2074,12 +292,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
2074
292
  ReactiveFormsModule,
2075
293
  UiIconModule,
2076
294
  UiUserTopModule,
2077
- TooltipModule,
2078
- UiAssistantModule
295
+ TooltipModule
2079
296
  ],
2080
297
  exports: [
2081
298
  UiHeaderComponent,
2082
- UiAssistantModule,
2083
299
  ]
2084
300
  }]
2085
301
  }] });
@@ -2092,5 +308,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
2092
308
  * Generated bundle index. Do not edit.
2093
309
  */
2094
310
 
2095
- export { UINY_AVATAR_PADRAO, UINY_CONFIG, UINY_LADO_A_LADO_PADRAO, UINY_LARGURA_MAX, UINY_LARGURA_MIN, UINY_LARGURA_PADRAO, UINY_MENSAGEM_MAX_CARACTERES, UINY_SUGESTOES, UINY_TIMEOUT_ACAO_MS, UINY_TIMEOUT_CONSULTA_MS, UINY_TIMEOUT_MENSAGEM_MS, UiAssistantActionCardComponent, UiAssistantButtonComponent, UiAssistantComposerComponent, UiAssistantLayoutComponent, UiAssistantMessagesComponent, UiAssistantModule, UiAssistantPanelComponent, UiAssistantService, UiAssistantTextComponent, UiHeaderComponent, UiHeaderModule, UiHeaderService, UinyApiService, UinyChatService, UinySessionService, larguraPainel, resolverUrlUiny };
311
+ export { UiHeaderComponent, UiHeaderModule, UiHeaderService };
2096
312
  //# sourceMappingURL=uitec-ng-header.mjs.map