@senior-gestao-relacionamento/angular-components 2.3.0 → 2.4.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.
- package/esm2022/lib/components/chatbot/chatbot.component.mjs +198 -0
- package/esm2022/lib/components/chatbot/pipes/markdown.pipe.mjs +48 -0
- package/esm2022/lib/components/chatbot/services/chatbot.service.mjs +30 -0
- package/esm2022/lib/i18n/en-US.json +10 -1
- package/esm2022/lib/i18n/es-ES.json +10 -1
- package/esm2022/lib/i18n/pt-BR.json +10 -1
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/senior-gestao-relacionamento-angular-components.mjs +303 -16
- package/fesm2022/senior-gestao-relacionamento-angular-components.mjs.map +1 -1
- package/lib/components/chatbot/chatbot.component.d.ts +49 -0
- package/lib/components/chatbot/pipes/markdown.pipe.d.ts +23 -0
- package/lib/components/chatbot/services/chatbot.service.d.ts +17 -0
- package/package.json +4 -2
- package/public-api.d.ts +2 -0
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { NgModule, Injectable,
|
|
3
|
-
import * as
|
|
2
|
+
import { NgModule, Injectable, Pipe, inject, NgZone, Component, ViewChild, EventEmitter, Input, Output } from '@angular/core';
|
|
3
|
+
import * as i1$2 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import { BehaviorSubject, of, tap, Subject, debounceTime, switchMap } from 'rxjs';
|
|
6
6
|
import * as i1 from '@angular/common/http';
|
|
7
7
|
import { HttpParams } from '@angular/common/http';
|
|
8
|
-
import * as i1$
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
8
|
+
import * as i1$3 from '@angular/forms';
|
|
9
|
+
import { FormsModule, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
10
|
+
import { trigger, transition, style, animate } from '@angular/animations';
|
|
11
|
+
import * as i3 from 'primeng/button';
|
|
11
12
|
import { ButtonModule } from 'primeng/button';
|
|
13
|
+
import { Textarea } from 'primeng/textarea';
|
|
14
|
+
import { BadgeModule } from 'primeng/badge';
|
|
15
|
+
import * as i5 from '@seniorsistemas/components-ai';
|
|
16
|
+
import { WebSocketService, TranslationService, TranslatePipe, DynamicFormComponent, DateFormatPipe } from '@seniorsistemas/components-ai';
|
|
17
|
+
import DOMPurify from 'dompurify';
|
|
18
|
+
import { marked } from 'marked';
|
|
19
|
+
import * as i1$1 from '@angular/platform-browser';
|
|
12
20
|
import * as i4 from 'primeng/dialog';
|
|
13
21
|
import { DialogModule } from 'primeng/dialog';
|
|
14
22
|
import * as i9 from 'primeng/drawer';
|
|
@@ -19,7 +27,7 @@ import * as i11 from 'primeng/inputnumber';
|
|
|
19
27
|
import { InputNumberModule } from 'primeng/inputnumber';
|
|
20
28
|
import * as i12 from 'primeng/multiselect';
|
|
21
29
|
import { MultiSelectModule } from 'primeng/multiselect';
|
|
22
|
-
import * as i7
|
|
30
|
+
import * as i7 from 'primeng/tag';
|
|
23
31
|
import { TagModule } from 'primeng/tag';
|
|
24
32
|
import * as i13 from 'primeng/tooltip';
|
|
25
33
|
import { TooltipModule } from 'primeng/tooltip';
|
|
@@ -29,8 +37,6 @@ import * as i15 from 'primeng/table';
|
|
|
29
37
|
import { TableModule } from 'primeng/table';
|
|
30
38
|
import * as i16 from 'primeng/autocomplete';
|
|
31
39
|
import { AutoCompleteModule } from 'primeng/autocomplete';
|
|
32
|
-
import * as i5 from '@seniorsistemas/components-ai';
|
|
33
|
-
import { TranslatePipe, DynamicFormComponent, DateFormatPipe } from '@seniorsistemas/components-ai';
|
|
34
40
|
import * as i8 from 'primeng/api';
|
|
35
41
|
|
|
36
42
|
/**
|
|
@@ -344,7 +350,16 @@ var ptBR = {
|
|
|
344
350
|
"crmx.components.schedule_status": "Status",
|
|
345
351
|
"crmx.components.schedule_confirmation_yes": "Confirmado",
|
|
346
352
|
"crmx.components.schedule_confirmation_no": "Recusado",
|
|
347
|
-
"crmx.components.schedule_confirmation_pending": "Pendente"
|
|
353
|
+
"crmx.components.schedule_confirmation_pending": "Pendente",
|
|
354
|
+
"crmx.components.chatbot_title": "Assistente IA",
|
|
355
|
+
"crmx.components.chatbot_welcome": "Olá! Sou o assistente virtual do CRMX. Como posso ajudar você?",
|
|
356
|
+
"crmx.components.chatbot_placeholder": "Digite sua mensagem...",
|
|
357
|
+
"crmx.components.chatbot_error": "Ocorreu um erro ao processar sua mensagem. Tente novamente.",
|
|
358
|
+
"crmx.components.chatbot_typing_1": "Procurando...",
|
|
359
|
+
"crmx.components.chatbot_typing_2": "Analisando sua pergunta...",
|
|
360
|
+
"crmx.components.chatbot_typing_3": "Reunindo informações...",
|
|
361
|
+
"crmx.components.chatbot_typing_4": "Quase pronto!",
|
|
362
|
+
"crmx.components.chatbot_typing_5": "Finalizando a resposta..."
|
|
348
363
|
};
|
|
349
364
|
|
|
350
365
|
var enUS = {
|
|
@@ -446,7 +461,16 @@ var enUS = {
|
|
|
446
461
|
"crmx.components.schedule_status": "Status",
|
|
447
462
|
"crmx.components.schedule_confirmation_yes": "Confirmed",
|
|
448
463
|
"crmx.components.schedule_confirmation_no": "Declined",
|
|
449
|
-
"crmx.components.schedule_confirmation_pending": "Pending"
|
|
464
|
+
"crmx.components.schedule_confirmation_pending": "Pending",
|
|
465
|
+
"crmx.components.chatbot_title": "AI Assistant",
|
|
466
|
+
"crmx.components.chatbot_welcome": "Hello! I'm the CRMX virtual assistant. How can I help you?",
|
|
467
|
+
"crmx.components.chatbot_placeholder": "Type your message...",
|
|
468
|
+
"crmx.components.chatbot_error": "An error occurred while processing your message. Please try again.",
|
|
469
|
+
"crmx.components.chatbot_typing_1": "Searching...",
|
|
470
|
+
"crmx.components.chatbot_typing_2": "Analyzing your question...",
|
|
471
|
+
"crmx.components.chatbot_typing_3": "Gathering information...",
|
|
472
|
+
"crmx.components.chatbot_typing_4": "Almost there!",
|
|
473
|
+
"crmx.components.chatbot_typing_5": "Wrapping up the answer..."
|
|
450
474
|
};
|
|
451
475
|
|
|
452
476
|
var esES = {
|
|
@@ -548,7 +572,16 @@ var esES = {
|
|
|
548
572
|
"crmx.components.schedule_status": "Estado",
|
|
549
573
|
"crmx.components.schedule_confirmation_yes": "Confirmado",
|
|
550
574
|
"crmx.components.schedule_confirmation_no": "Rechazado",
|
|
551
|
-
"crmx.components.schedule_confirmation_pending": "Pendiente"
|
|
575
|
+
"crmx.components.schedule_confirmation_pending": "Pendiente",
|
|
576
|
+
"crmx.components.chatbot_title": "Asistente IA",
|
|
577
|
+
"crmx.components.chatbot_welcome": "¡Hola! Soy el asistente virtual de CRMX. ¿Cómo puedo ayudarte?",
|
|
578
|
+
"crmx.components.chatbot_placeholder": "Escribe tu mensaje...",
|
|
579
|
+
"crmx.components.chatbot_error": "Ocurrió un error al procesar tu mensaje. Inténtalo de nuevo.",
|
|
580
|
+
"crmx.components.chatbot_typing_1": "Buscando...",
|
|
581
|
+
"crmx.components.chatbot_typing_2": "Analizando tu pregunta...",
|
|
582
|
+
"crmx.components.chatbot_typing_3": "Reuniendo información...",
|
|
583
|
+
"crmx.components.chatbot_typing_4": "¡Casi listo!",
|
|
584
|
+
"crmx.components.chatbot_typing_5": "Finalizando la respuesta..."
|
|
552
585
|
};
|
|
553
586
|
|
|
554
587
|
const LIB_TRANSLATIONS = {
|
|
@@ -590,6 +623,260 @@ function provideAngularComponentsTranslations(appLoader) {
|
|
|
590
623
|
};
|
|
591
624
|
}
|
|
592
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Serviço de integração com o backend do chatbot.
|
|
628
|
+
*
|
|
629
|
+
* Endpoint fixo: `crmx/chatbot/signals/chat` (compartilhado por todos os
|
|
630
|
+
* frontends do CRM).
|
|
631
|
+
*/
|
|
632
|
+
class ChatbotService {
|
|
633
|
+
http;
|
|
634
|
+
signalsUrl = 'crmx/chatbot/signals';
|
|
635
|
+
constructor(http) {
|
|
636
|
+
this.http = http;
|
|
637
|
+
}
|
|
638
|
+
chat(message, chatId) {
|
|
639
|
+
const payload = { message };
|
|
640
|
+
if (chatId) {
|
|
641
|
+
payload.chatId = chatId;
|
|
642
|
+
}
|
|
643
|
+
return this.http.post(`${this.signalsUrl}/chat`, payload);
|
|
644
|
+
}
|
|
645
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
646
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, providedIn: 'root' });
|
|
647
|
+
}
|
|
648
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotService, decorators: [{
|
|
649
|
+
type: Injectable,
|
|
650
|
+
args: [{ providedIn: 'root' }]
|
|
651
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Pipe interno do chatbot para renderizar Markdown como HTML seguro.
|
|
655
|
+
*
|
|
656
|
+
* Fluxo de segurança:
|
|
657
|
+
* 1. `marked.parse` converte Markdown em HTML (sem JS executável por padrão).
|
|
658
|
+
* 2. `DOMPurify.sanitize` remove qualquer tag/atributo potencialmente
|
|
659
|
+
* malicioso (script, on*, javascript:..., etc.).
|
|
660
|
+
* 3. `bypassSecurityTrustHtml` libera o HTML já sanitizado para o
|
|
661
|
+
* `[innerHTML]` sem que o Angular faça uma segunda sanitização que
|
|
662
|
+
* removeria estilos e classes legítimas do markdown.
|
|
663
|
+
*
|
|
664
|
+
* Não exportado no public-api: uso restrito ao componente de chatbot.
|
|
665
|
+
*/
|
|
666
|
+
class ChatbotMarkdownPipe {
|
|
667
|
+
sanitizer;
|
|
668
|
+
constructor(sanitizer) {
|
|
669
|
+
this.sanitizer = sanitizer;
|
|
670
|
+
marked.setOptions({
|
|
671
|
+
breaks: true,
|
|
672
|
+
gfm: true
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
transform(value) {
|
|
676
|
+
if (!value) {
|
|
677
|
+
return '';
|
|
678
|
+
}
|
|
679
|
+
const rawHtml = marked.parse(value);
|
|
680
|
+
const cleanHtml = DOMPurify.sanitize(rawHtml);
|
|
681
|
+
// HTML sanitized via DOMPurify above; the bypass is required so the
|
|
682
|
+
// already-safe markdown styles aren't stripped by Angular's sanitizer.
|
|
683
|
+
return this.sanitizer.bypassSecurityTrustHtml(cleanHtml);
|
|
684
|
+
}
|
|
685
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
|
|
686
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, isStandalone: true, name: "sChatbotMarkdown" });
|
|
687
|
+
}
|
|
688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotMarkdownPipe, decorators: [{
|
|
689
|
+
type: Pipe,
|
|
690
|
+
args: [{
|
|
691
|
+
name: 'sChatbotMarkdown',
|
|
692
|
+
standalone: true
|
|
693
|
+
}]
|
|
694
|
+
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }] });
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Delay (ms) used before scrolling the chat container to the bottom.
|
|
698
|
+
* Gives Angular time to render the newly pushed message into the DOM.
|
|
699
|
+
*/
|
|
700
|
+
const SCROLL_TO_BOTTOM_DELAY_MS = 50;
|
|
701
|
+
/**
|
|
702
|
+
* Interval (ms) between rotations of the "typing" status phrase.
|
|
703
|
+
*/
|
|
704
|
+
const TYPING_PHRASE_ROTATION_MS = 2500;
|
|
705
|
+
/**
|
|
706
|
+
* Translation keys cycled while the assistant is preparing a response.
|
|
707
|
+
* Order matters: phrases are shown in this sequence.
|
|
708
|
+
*/
|
|
709
|
+
const TYPING_PHRASE_KEYS = [
|
|
710
|
+
'crmx.components.chatbot_typing_1',
|
|
711
|
+
'crmx.components.chatbot_typing_2',
|
|
712
|
+
'crmx.components.chatbot_typing_3',
|
|
713
|
+
'crmx.components.chatbot_typing_4',
|
|
714
|
+
'crmx.components.chatbot_typing_5'
|
|
715
|
+
];
|
|
716
|
+
/**
|
|
717
|
+
* Chatbot global do CRM.
|
|
718
|
+
*
|
|
719
|
+
* Standalone, plug-and-play: basta declarar `<s-chatbot></s-chatbot>` no
|
|
720
|
+
* `app.component.html` da aplicação consumidora. Toda a integração
|
|
721
|
+
* (WebSocket de respostas, REST de envio, traduções e renderização de
|
|
722
|
+
* Markdown) está encapsulada na biblioteca.
|
|
723
|
+
*/
|
|
724
|
+
class ChatbotComponent {
|
|
725
|
+
chatMessagesContainer;
|
|
726
|
+
isOpen = false;
|
|
727
|
+
userInput = '';
|
|
728
|
+
sendingMessage = false;
|
|
729
|
+
chatMessages = [];
|
|
730
|
+
chatId = null;
|
|
731
|
+
unreadCount = 0;
|
|
732
|
+
typingPhraseKey = TYPING_PHRASE_KEYS[0];
|
|
733
|
+
wsSubscription = null;
|
|
734
|
+
typingPhraseTimer = null;
|
|
735
|
+
typingPhraseIndex = 0;
|
|
736
|
+
wsConfig = {
|
|
737
|
+
domain: 'crmx',
|
|
738
|
+
service: 'chatbot',
|
|
739
|
+
primitive: 'wsChatResponse',
|
|
740
|
+
userScoped: true
|
|
741
|
+
};
|
|
742
|
+
webSocketService = inject(WebSocketService);
|
|
743
|
+
ngZone = inject(NgZone);
|
|
744
|
+
chatbotService = inject(ChatbotService);
|
|
745
|
+
translationService = inject(TranslationService);
|
|
746
|
+
ngOnInit() {
|
|
747
|
+
this.subscribeToWebSocket();
|
|
748
|
+
}
|
|
749
|
+
ngOnDestroy() {
|
|
750
|
+
this.wsSubscription?.unsubscribe();
|
|
751
|
+
this.webSocketService.unsubscribe(this.wsConfig);
|
|
752
|
+
this.webSocketService.disconnect();
|
|
753
|
+
this.stopTypingPhraseRotation();
|
|
754
|
+
}
|
|
755
|
+
toggleChat() {
|
|
756
|
+
this.isOpen = !this.isOpen;
|
|
757
|
+
if (this.isOpen) {
|
|
758
|
+
this.unreadCount = 0;
|
|
759
|
+
setTimeout(() => this.scrollChatToBottom(), 100);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
sendMessage() {
|
|
763
|
+
const text = this.userInput.trim();
|
|
764
|
+
if (!text || this.sendingMessage) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
this.chatMessages.push({ role: 'user', content: text, timestamp: new Date() });
|
|
768
|
+
this.userInput = '';
|
|
769
|
+
this.sendingMessage = true;
|
|
770
|
+
this.startTypingPhraseRotation();
|
|
771
|
+
this.scrollChatToBottom();
|
|
772
|
+
this.chatbotService.chat(text, this.chatId ?? undefined).subscribe({
|
|
773
|
+
error: () => {
|
|
774
|
+
this.chatMessages.push({
|
|
775
|
+
role: 'assistant',
|
|
776
|
+
content: this.translationService.translate('crmx.components.chatbot_error'),
|
|
777
|
+
timestamp: new Date()
|
|
778
|
+
});
|
|
779
|
+
this.sendingMessage = false;
|
|
780
|
+
this.stopTypingPhraseRotation();
|
|
781
|
+
this.scrollChatToBottom();
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
onChatKeydown(event) {
|
|
786
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
787
|
+
event.preventDefault();
|
|
788
|
+
this.sendMessage();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
subscribeToWebSocket() {
|
|
792
|
+
this.wsSubscription = this.webSocketService
|
|
793
|
+
.subscribe(this.wsConfig)
|
|
794
|
+
.subscribe(msg => {
|
|
795
|
+
const data = msg.body.data;
|
|
796
|
+
this.ngZone.run(() => {
|
|
797
|
+
if (!data) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (data.chatId) {
|
|
801
|
+
this.chatId = data.chatId;
|
|
802
|
+
}
|
|
803
|
+
this.chatMessages.push({
|
|
804
|
+
role: 'assistant',
|
|
805
|
+
content: data.response,
|
|
806
|
+
timestamp: new Date(),
|
|
807
|
+
responseTime: data.responseTime
|
|
808
|
+
});
|
|
809
|
+
this.sendingMessage = false;
|
|
810
|
+
this.stopTypingPhraseRotation();
|
|
811
|
+
if (!this.isOpen) {
|
|
812
|
+
this.unreadCount++;
|
|
813
|
+
}
|
|
814
|
+
this.scrollChatToBottom();
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
scrollChatToBottom() {
|
|
819
|
+
setTimeout(() => {
|
|
820
|
+
const el = this.chatMessagesContainer?.nativeElement;
|
|
821
|
+
if (el) {
|
|
822
|
+
el.scrollTop = el.scrollHeight;
|
|
823
|
+
}
|
|
824
|
+
}, SCROLL_TO_BOTTOM_DELAY_MS);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Starts cycling through the typing phrases. Always begins from the first
|
|
828
|
+
* key so the user sees the same friendly opening every time.
|
|
829
|
+
*/
|
|
830
|
+
startTypingPhraseRotation() {
|
|
831
|
+
this.stopTypingPhraseRotation();
|
|
832
|
+
this.typingPhraseIndex = 0;
|
|
833
|
+
this.typingPhraseKey = TYPING_PHRASE_KEYS[0];
|
|
834
|
+
this.typingPhraseTimer = setInterval(() => {
|
|
835
|
+
this.ngZone.run(() => {
|
|
836
|
+
this.typingPhraseIndex = (this.typingPhraseIndex + 1) % TYPING_PHRASE_KEYS.length;
|
|
837
|
+
this.typingPhraseKey = TYPING_PHRASE_KEYS[this.typingPhraseIndex];
|
|
838
|
+
});
|
|
839
|
+
}, TYPING_PHRASE_ROTATION_MS);
|
|
840
|
+
}
|
|
841
|
+
stopTypingPhraseRotation() {
|
|
842
|
+
if (this.typingPhraseTimer !== null) {
|
|
843
|
+
clearInterval(this.typingPhraseTimer);
|
|
844
|
+
this.typingPhraseTimer = null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
848
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChatbotComponent, isStandalone: true, selector: "s-chatbot", viewQueries: [{ propertyName: "chatMessagesContainer", first: true, predicate: ["chatMessagesContainer"], descendants: true }], ngImport: i0, template: "<!-- Floating Action Button -->\n<button type=\"button\" class=\"chatbot-fab\" [class.open]=\"isOpen\"\n [attr.aria-label]=\"'crmx.components.chatbot_title' | translate\"\n [attr.aria-expanded]=\"isOpen\"\n (click)=\"toggleChat()\">\n <i *ngIf=\"!isOpen\" class=\"pi pi-lightbulb fab-icon\"></i>\n <i *ngIf=\"isOpen\" class=\"pi pi-times\"></i>\n <span class=\"unread-badge\" *ngIf=\"unreadCount > 0 && !isOpen\">{{ unreadCount }}</span>\n</button>\n\n<!-- Chat Window -->\n<div class=\"chatbot-window\" *ngIf=\"isOpen\">\n <div class=\"chatbot-header\">\n <i class=\"pi pi-lightbulb header-icon\"></i>\n <span>{{ 'crmx.components.chatbot_title' | translate }}</span>\n </div>\n\n <div class=\"chatbot-messages\" #chatMessagesContainer>\n <!-- Welcome -->\n <div class=\"chatbot-welcome\" *ngIf=\"chatMessages.length === 0\">\n <i class=\"pi pi-lightbulb welcome-icon\"></i>\n <p>{{ 'crmx.components.chatbot_welcome' | translate }}</p>\n </div>\n\n <!-- Messages -->\n <div *ngFor=\"let msg of chatMessages\" class=\"chatbot-message\"\n [ngClass]=\"{ 'user-message': msg.role === 'user', 'assistant-message': msg.role === 'assistant' }\">\n <div class=\"message-avatar\">\n <i *ngIf=\"msg.role === 'user'\" class=\"pi pi-user\"></i>\n <i *ngIf=\"msg.role === 'assistant'\" class=\"pi pi-lightbulb assistant-avatar-icon\"></i>\n </div>\n <div class=\"message-bubble\" [class.markdown-content]=\"msg.role === 'assistant'\">\n <p *ngIf=\"msg.role === 'user'\">{{ msg.content }}</p>\n <div *ngIf=\"msg.role === 'assistant'\" [innerHTML]=\"msg.content | sChatbotMarkdown\"></div>\n <div class=\"message-meta\">\n <span class=\"message-time\">{{ msg.timestamp | date:'HH:mm' }}</span>\n <span class=\"response-time\" *ngIf=\"msg.responseTime\">\u23F1 {{ msg.responseTime }}s</span>\n </div>\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"chatbot-typing\" *ngIf=\"sendingMessage\">\n <div class=\"message-avatar\"><i class=\"pi pi-lightbulb assistant-avatar-icon\"></i></div>\n <div class=\"typing-bubble\" aria-live=\"polite\">\n <span class=\"typing-phrase\" [@chatbotPhraseFade]=\"typingPhraseKey\">{{ typingPhraseKey | translate }}</span>\n <span class=\"typing-pulse\" aria-hidden=\"true\">\n <span></span><span></span><span></span>\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"chatbot-input-area\">\n <div class=\"input-row\">\n <label for=\"s-chatbot-input\" class=\"sr-only\">{{ 'crmx.components.chatbot_placeholder' | translate }}</label>\n <textarea pTextarea id=\"s-chatbot-input\" [(ngModel)]=\"userInput\"\n [placeholder]=\"'crmx.components.chatbot_placeholder' | translate\"\n [autoResize]=\"true\" [rows]=\"1\" [maxlength]=\"2000\"\n (keydown)=\"onChatKeydown($event)\" [disabled]=\"sendingMessage\"></textarea>\n <button pButton type=\"button\" class=\"p-button-rounded send-btn\"\n [attr.aria-label]=\"'crmx.components.chatbot_placeholder' | translate\"\n [disabled]=\"!userInput.trim() || sendingMessage\"\n (click)=\"sendMessage()\">\n <i class=\"pi pi-send\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.chatbot-fab{position:fixed;bottom:1.5rem;right:1.5rem;width:56px;height:56px;border:0;padding:0;border-radius:50%;background:var(--p-primary-color, #667eea);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #00000040;z-index:1100;transition:transform .2s ease,background .2s ease;font:inherit}.chatbot-fab:hover{transform:scale(1.08)}.chatbot-fab:focus-visible{outline:2px solid var(--p-primary-color, #667eea);outline-offset:2px}.chatbot-fab.open{background:var(--p-surface-500, #6c757d)}.chatbot-fab .pi-times{font-size:1.25rem}.chatbot-fab .fab-icon{font-size:1.5rem}.chatbot-fab .unread-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;border-radius:10px;background:#ef4444;color:#fff;font-size:.7rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 5px;box-shadow:0 2px 4px #0003}.chatbot-window{position:fixed;bottom:6.5rem;right:1.5rem;width:380px;height:520px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000002e;z-index:1100;display:flex;flex-direction:column;overflow:hidden;animation:chatbot-slide-up .25s ease-out}.chatbot-header{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;background:var(--p-primary-color, #667eea);color:#fff;font-weight:600;font-size:.9rem;flex-shrink:0}.chatbot-header .header-icon{font-size:1.1rem}.chatbot-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.chatbot-welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#adb5bd;text-align:center;padding:2rem}.chatbot-welcome .welcome-icon{font-size:2.5rem;color:var(--p-primary-color, #667eea);opacity:.7}.chatbot-welcome p{font-size:.9rem;color:#6c757d;margin:.75rem 0 0;line-height:1.5}.chatbot-message{display:flex;gap:.5rem;max-width:90%;align-items:flex-end}.message-avatar{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .assistant-avatar-icon{font-size:1rem;color:var(--p-primary-color, #667eea)}.message-bubble{padding:.6rem .85rem;font-size:.85rem;line-height:1.4}.message-bubble p{margin:0;white-space:pre-wrap;word-break:break-word}.message-bubble .response-time{opacity:.7}.message-bubble .message-meta{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.7rem}.chatbot-message.assistant-message{align-self:flex-start}.chatbot-message.assistant-message .message-bubble{background:#f1f3f5;color:#2c3e50;border-radius:12px 12px 12px 0}.chatbot-message.assistant-message .message-bubble .message-time{color:#adb5bd}.chatbot-message.user-message{align-self:flex-end;flex-direction:row-reverse}.chatbot-message.user-message .message-avatar{background:var(--p-primary-color, #667eea);color:#fff}.chatbot-message.user-message .message-bubble{background:var(--p-primary-color, #667eea);color:#fff;border-radius:12px 12px 0}.chatbot-message.user-message .message-bubble .message-meta{justify-content:flex-end}.chatbot-message.user-message .message-bubble .message-time{color:#ffffffb3}.chatbot-typing{display:flex;gap:.5rem;align-self:flex-start;align-items:flex-end}.chatbot-typing .typing-bubble{display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:#f1f3f5;color:#495057;border-radius:12px 12px 12px 0;font-size:.85rem;line-height:1.3;max-width:240px}.chatbot-typing .typing-phrase{display:inline-block}.chatbot-typing .typing-pulse{display:inline-flex;gap:3px;flex-shrink:0}.chatbot-typing .typing-pulse span{width:4px;height:4px;border-radius:50%;background:#adb5bd;animation:chatbot-typing 1.4s infinite}.chatbot-typing .typing-pulse span:nth-child(2){animation-delay:.2s}.chatbot-typing .typing-pulse span:nth-child(3){animation-delay:.4s}.chatbot-input-area{display:flex;flex-direction:column;gap:.5rem;padding:.75rem;border-top:1px solid #e9ecef;background:#fff;flex-shrink:0}.chatbot-input-area .input-row{display:flex;align-items:flex-end;gap:.5rem}.chatbot-input-area textarea{flex:1;resize:none;border-radius:8px;font-size:.85rem;max-height:100px}.chatbot-input-area .send-btn{flex-shrink:0;width:36px;height:36px;align-self:center}:host ::ng-deep .message-bubble.markdown-content{overflow-x:auto}:host ::ng-deep .message-bubble.markdown-content>*{margin-top:0;margin-bottom:0}:host ::ng-deep .message-bubble.markdown-content>*+*{margin-top:.4rem}:host ::ng-deep .message-bubble.markdown-content a{color:var(--p-primary-color, #667eea);text-decoration:underline}:host ::ng-deep .message-bubble.markdown-content strong{font-weight:600}:host ::ng-deep .message-bubble.markdown-content hr{border:0;border-top:1px solid rgba(0,0,0,.12);margin:.5rem 0}:host ::ng-deep .message-bubble.markdown-content code{background:#00000014;padding:.1rem .3rem;border-radius:3px;font-size:.8rem;font-family:Courier New,monospace}:host ::ng-deep .message-bubble.markdown-content pre{background:#0000000f;padding:.5rem;border-radius:6px;overflow-x:auto;font-size:.8rem}:host ::ng-deep .message-bubble.markdown-content pre code{background:none;padding:0}:host ::ng-deep .message-bubble.markdown-content blockquote{border-left:3px solid rgba(0,0,0,.15);padding-left:.6rem;opacity:.85}:host ::ng-deep .message-bubble.markdown-content ul,:host ::ng-deep .message-bubble.markdown-content ol{padding-left:1.2rem}:host ::ng-deep .message-bubble.markdown-content li{margin-bottom:.15rem}:host ::ng-deep .message-bubble.markdown-content h1,:host ::ng-deep .message-bubble.markdown-content h2,:host ::ng-deep .message-bubble.markdown-content h3,:host ::ng-deep .message-bubble.markdown-content h4,:host ::ng-deep .message-bubble.markdown-content h5,:host ::ng-deep .message-bubble.markdown-content h6{font-weight:600;line-height:1.3}:host ::ng-deep .message-bubble.markdown-content h1{font-size:1.1rem}:host ::ng-deep .message-bubble.markdown-content h2{font-size:1rem}:host ::ng-deep .message-bubble.markdown-content h3{font-size:.95rem}:host ::ng-deep .message-bubble.markdown-content table{border-collapse:collapse;width:100%;font-size:.8rem;margin:.4rem 0}:host ::ng-deep .message-bubble.markdown-content table th,:host ::ng-deep .message-bubble.markdown-content table td{border:1px solid rgba(0,0,0,.2);padding:.3rem .5rem;text-align:left;vertical-align:top}:host ::ng-deep .message-bubble.markdown-content table th{font-weight:600;background:#0000000d}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th,:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table td{border-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th{background:#ffffff14}:host ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#00000005}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#ffffff08}@keyframes chatbot-slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes chatbot-typing{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}:host-context(.app-dark) .chatbot-window{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-messages{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-welcome p{color:var(--p-surface-500, #adb5bd)}:host-context(.app-dark) .chatbot-message.assistant-message .message-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) .chatbot-input-area{background:var(--p-surface-100, #1e1e1e);border-top-color:var(--p-surface-300, #3d3d3d)}:host-context(.app-dark) .chatbot-typing .typing-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content code{background:#ffffff1a}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content pre{background:#ffffff14}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content blockquote{border-left-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content hr{border-top-color:#ffffff26}@media (max-width: 480px){.chatbot-window{width:calc(100vw - 2rem);height:calc(100vh - 8rem);right:1rem;bottom:5rem}.chatbot-fab{bottom:1rem;right:1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: "pipe", type: i1$2.DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "directive", type: Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: ChatbotMarkdownPipe, name: "sChatbotMarkdown" }], animations: [
|
|
849
|
+
trigger('chatbotPhraseFade', [
|
|
850
|
+
transition('* => *', [
|
|
851
|
+
style({ opacity: 0, transform: 'translateY(2px)' }),
|
|
852
|
+
animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
|
|
853
|
+
])
|
|
854
|
+
])
|
|
855
|
+
] });
|
|
856
|
+
}
|
|
857
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatbotComponent, decorators: [{
|
|
858
|
+
type: Component,
|
|
859
|
+
args: [{ selector: 's-chatbot', standalone: true, imports: [
|
|
860
|
+
CommonModule,
|
|
861
|
+
FormsModule,
|
|
862
|
+
ButtonModule,
|
|
863
|
+
Textarea,
|
|
864
|
+
BadgeModule,
|
|
865
|
+
TranslatePipe,
|
|
866
|
+
ChatbotMarkdownPipe
|
|
867
|
+
], animations: [
|
|
868
|
+
trigger('chatbotPhraseFade', [
|
|
869
|
+
transition('* => *', [
|
|
870
|
+
style({ opacity: 0, transform: 'translateY(2px)' }),
|
|
871
|
+
animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' }))
|
|
872
|
+
])
|
|
873
|
+
])
|
|
874
|
+
], template: "<!-- Floating Action Button -->\n<button type=\"button\" class=\"chatbot-fab\" [class.open]=\"isOpen\"\n [attr.aria-label]=\"'crmx.components.chatbot_title' | translate\"\n [attr.aria-expanded]=\"isOpen\"\n (click)=\"toggleChat()\">\n <i *ngIf=\"!isOpen\" class=\"pi pi-lightbulb fab-icon\"></i>\n <i *ngIf=\"isOpen\" class=\"pi pi-times\"></i>\n <span class=\"unread-badge\" *ngIf=\"unreadCount > 0 && !isOpen\">{{ unreadCount }}</span>\n</button>\n\n<!-- Chat Window -->\n<div class=\"chatbot-window\" *ngIf=\"isOpen\">\n <div class=\"chatbot-header\">\n <i class=\"pi pi-lightbulb header-icon\"></i>\n <span>{{ 'crmx.components.chatbot_title' | translate }}</span>\n </div>\n\n <div class=\"chatbot-messages\" #chatMessagesContainer>\n <!-- Welcome -->\n <div class=\"chatbot-welcome\" *ngIf=\"chatMessages.length === 0\">\n <i class=\"pi pi-lightbulb welcome-icon\"></i>\n <p>{{ 'crmx.components.chatbot_welcome' | translate }}</p>\n </div>\n\n <!-- Messages -->\n <div *ngFor=\"let msg of chatMessages\" class=\"chatbot-message\"\n [ngClass]=\"{ 'user-message': msg.role === 'user', 'assistant-message': msg.role === 'assistant' }\">\n <div class=\"message-avatar\">\n <i *ngIf=\"msg.role === 'user'\" class=\"pi pi-user\"></i>\n <i *ngIf=\"msg.role === 'assistant'\" class=\"pi pi-lightbulb assistant-avatar-icon\"></i>\n </div>\n <div class=\"message-bubble\" [class.markdown-content]=\"msg.role === 'assistant'\">\n <p *ngIf=\"msg.role === 'user'\">{{ msg.content }}</p>\n <div *ngIf=\"msg.role === 'assistant'\" [innerHTML]=\"msg.content | sChatbotMarkdown\"></div>\n <div class=\"message-meta\">\n <span class=\"message-time\">{{ msg.timestamp | date:'HH:mm' }}</span>\n <span class=\"response-time\" *ngIf=\"msg.responseTime\">\u23F1 {{ msg.responseTime }}s</span>\n </div>\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"chatbot-typing\" *ngIf=\"sendingMessage\">\n <div class=\"message-avatar\"><i class=\"pi pi-lightbulb assistant-avatar-icon\"></i></div>\n <div class=\"typing-bubble\" aria-live=\"polite\">\n <span class=\"typing-phrase\" [@chatbotPhraseFade]=\"typingPhraseKey\">{{ typingPhraseKey | translate }}</span>\n <span class=\"typing-pulse\" aria-hidden=\"true\">\n <span></span><span></span><span></span>\n </span>\n </div>\n </div>\n </div>\n\n <div class=\"chatbot-input-area\">\n <div class=\"input-row\">\n <label for=\"s-chatbot-input\" class=\"sr-only\">{{ 'crmx.components.chatbot_placeholder' | translate }}</label>\n <textarea pTextarea id=\"s-chatbot-input\" [(ngModel)]=\"userInput\"\n [placeholder]=\"'crmx.components.chatbot_placeholder' | translate\"\n [autoResize]=\"true\" [rows]=\"1\" [maxlength]=\"2000\"\n (keydown)=\"onChatKeydown($event)\" [disabled]=\"sendingMessage\"></textarea>\n <button pButton type=\"button\" class=\"p-button-rounded send-btn\"\n [attr.aria-label]=\"'crmx.components.chatbot_placeholder' | translate\"\n [disabled]=\"!userInput.trim() || sendingMessage\"\n (click)=\"sendMessage()\">\n <i class=\"pi pi-send\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.chatbot-fab{position:fixed;bottom:1.5rem;right:1.5rem;width:56px;height:56px;border:0;padding:0;border-radius:50%;background:var(--p-primary-color, #667eea);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #00000040;z-index:1100;transition:transform .2s ease,background .2s ease;font:inherit}.chatbot-fab:hover{transform:scale(1.08)}.chatbot-fab:focus-visible{outline:2px solid var(--p-primary-color, #667eea);outline-offset:2px}.chatbot-fab.open{background:var(--p-surface-500, #6c757d)}.chatbot-fab .pi-times{font-size:1.25rem}.chatbot-fab .fab-icon{font-size:1.5rem}.chatbot-fab .unread-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;border-radius:10px;background:#ef4444;color:#fff;font-size:.7rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 5px;box-shadow:0 2px 4px #0003}.chatbot-window{position:fixed;bottom:6.5rem;right:1.5rem;width:380px;height:520px;background:#fff;border-radius:12px;box-shadow:0 8px 32px #0000002e;z-index:1100;display:flex;flex-direction:column;overflow:hidden;animation:chatbot-slide-up .25s ease-out}.chatbot-header{display:flex;align-items:center;gap:.5rem;padding:.75rem 1rem;background:var(--p-primary-color, #667eea);color:#fff;font-weight:600;font-size:.9rem;flex-shrink:0}.chatbot-header .header-icon{font-size:1.1rem}.chatbot-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.75rem}.chatbot-welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#adb5bd;text-align:center;padding:2rem}.chatbot-welcome .welcome-icon{font-size:2.5rem;color:var(--p-primary-color, #667eea);opacity:.7}.chatbot-welcome p{font-size:.9rem;color:#6c757d;margin:.75rem 0 0;line-height:1.5}.chatbot-message{display:flex;gap:.5rem;max-width:90%;align-items:flex-end}.message-avatar{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.message-avatar .assistant-avatar-icon{font-size:1rem;color:var(--p-primary-color, #667eea)}.message-bubble{padding:.6rem .85rem;font-size:.85rem;line-height:1.4}.message-bubble p{margin:0;white-space:pre-wrap;word-break:break-word}.message-bubble .response-time{opacity:.7}.message-bubble .message-meta{display:flex;justify-content:flex-start;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.7rem}.chatbot-message.assistant-message{align-self:flex-start}.chatbot-message.assistant-message .message-bubble{background:#f1f3f5;color:#2c3e50;border-radius:12px 12px 12px 0}.chatbot-message.assistant-message .message-bubble .message-time{color:#adb5bd}.chatbot-message.user-message{align-self:flex-end;flex-direction:row-reverse}.chatbot-message.user-message .message-avatar{background:var(--p-primary-color, #667eea);color:#fff}.chatbot-message.user-message .message-bubble{background:var(--p-primary-color, #667eea);color:#fff;border-radius:12px 12px 0}.chatbot-message.user-message .message-bubble .message-meta{justify-content:flex-end}.chatbot-message.user-message .message-bubble .message-time{color:#ffffffb3}.chatbot-typing{display:flex;gap:.5rem;align-self:flex-start;align-items:flex-end}.chatbot-typing .typing-bubble{display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:#f1f3f5;color:#495057;border-radius:12px 12px 12px 0;font-size:.85rem;line-height:1.3;max-width:240px}.chatbot-typing .typing-phrase{display:inline-block}.chatbot-typing .typing-pulse{display:inline-flex;gap:3px;flex-shrink:0}.chatbot-typing .typing-pulse span{width:4px;height:4px;border-radius:50%;background:#adb5bd;animation:chatbot-typing 1.4s infinite}.chatbot-typing .typing-pulse span:nth-child(2){animation-delay:.2s}.chatbot-typing .typing-pulse span:nth-child(3){animation-delay:.4s}.chatbot-input-area{display:flex;flex-direction:column;gap:.5rem;padding:.75rem;border-top:1px solid #e9ecef;background:#fff;flex-shrink:0}.chatbot-input-area .input-row{display:flex;align-items:flex-end;gap:.5rem}.chatbot-input-area textarea{flex:1;resize:none;border-radius:8px;font-size:.85rem;max-height:100px}.chatbot-input-area .send-btn{flex-shrink:0;width:36px;height:36px;align-self:center}:host ::ng-deep .message-bubble.markdown-content{overflow-x:auto}:host ::ng-deep .message-bubble.markdown-content>*{margin-top:0;margin-bottom:0}:host ::ng-deep .message-bubble.markdown-content>*+*{margin-top:.4rem}:host ::ng-deep .message-bubble.markdown-content a{color:var(--p-primary-color, #667eea);text-decoration:underline}:host ::ng-deep .message-bubble.markdown-content strong{font-weight:600}:host ::ng-deep .message-bubble.markdown-content hr{border:0;border-top:1px solid rgba(0,0,0,.12);margin:.5rem 0}:host ::ng-deep .message-bubble.markdown-content code{background:#00000014;padding:.1rem .3rem;border-radius:3px;font-size:.8rem;font-family:Courier New,monospace}:host ::ng-deep .message-bubble.markdown-content pre{background:#0000000f;padding:.5rem;border-radius:6px;overflow-x:auto;font-size:.8rem}:host ::ng-deep .message-bubble.markdown-content pre code{background:none;padding:0}:host ::ng-deep .message-bubble.markdown-content blockquote{border-left:3px solid rgba(0,0,0,.15);padding-left:.6rem;opacity:.85}:host ::ng-deep .message-bubble.markdown-content ul,:host ::ng-deep .message-bubble.markdown-content ol{padding-left:1.2rem}:host ::ng-deep .message-bubble.markdown-content li{margin-bottom:.15rem}:host ::ng-deep .message-bubble.markdown-content h1,:host ::ng-deep .message-bubble.markdown-content h2,:host ::ng-deep .message-bubble.markdown-content h3,:host ::ng-deep .message-bubble.markdown-content h4,:host ::ng-deep .message-bubble.markdown-content h5,:host ::ng-deep .message-bubble.markdown-content h6{font-weight:600;line-height:1.3}:host ::ng-deep .message-bubble.markdown-content h1{font-size:1.1rem}:host ::ng-deep .message-bubble.markdown-content h2{font-size:1rem}:host ::ng-deep .message-bubble.markdown-content h3{font-size:.95rem}:host ::ng-deep .message-bubble.markdown-content table{border-collapse:collapse;width:100%;font-size:.8rem;margin:.4rem 0}:host ::ng-deep .message-bubble.markdown-content table th,:host ::ng-deep .message-bubble.markdown-content table td{border:1px solid rgba(0,0,0,.2);padding:.3rem .5rem;text-align:left;vertical-align:top}:host ::ng-deep .message-bubble.markdown-content table th{font-weight:600;background:#0000000d}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th,:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table td{border-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table th{background:#ffffff14}:host ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#00000005}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content table tbody tr:nth-child(2n) td{background:#ffffff08}@keyframes chatbot-slide-up{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes chatbot-typing{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-4px);opacity:1}}:host-context(.app-dark) .chatbot-window{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-messages{background:var(--p-surface-100, #1e1e1e)}:host-context(.app-dark) .chatbot-welcome p{color:var(--p-surface-500, #adb5bd)}:host-context(.app-dark) .chatbot-message.assistant-message .message-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) .chatbot-input-area{background:var(--p-surface-100, #1e1e1e);border-top-color:var(--p-surface-300, #3d3d3d)}:host-context(.app-dark) .chatbot-typing .typing-bubble{background:var(--p-surface-200, #2d2d2d);color:var(--p-surface-900, #e0e0e0)}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content code{background:#ffffff1a}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content pre{background:#ffffff14}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content blockquote{border-left-color:#fff3}:host-context(.app-dark) ::ng-deep .message-bubble.markdown-content hr{border-top-color:#ffffff26}@media (max-width: 480px){.chatbot-window{width:calc(100vw - 2rem);height:calc(100vh - 8rem);right:1rem;bottom:5rem}.chatbot-fab{bottom:1rem;right:1rem}}\n"] }]
|
|
875
|
+
}], propDecorators: { chatMessagesContainer: [{
|
|
876
|
+
type: ViewChild,
|
|
877
|
+
args: ['chatMessagesContainer']
|
|
878
|
+
}] } });
|
|
879
|
+
|
|
593
880
|
var SchedulePriority;
|
|
594
881
|
(function (SchedulePriority) {
|
|
595
882
|
SchedulePriority["Low"] = "LOW";
|
|
@@ -1649,8 +1936,8 @@ class ScheduleFormComponent {
|
|
|
1649
1936
|
getDefaultEndTime(st) {
|
|
1650
1937
|
return getDefaultEndTime(st);
|
|
1651
1938
|
}
|
|
1652
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScheduleFormComponent, deps: [{ token: i1$
|
|
1653
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScheduleFormComponent, isStandalone: true, selector: "s-schedule-form", inputs: { visible: "visible", selectedDate: "selectedDate" }, outputs: { visibleChange: "visibleChange", saved: "saved" }, viewQueries: [{ propertyName: "detailFormComponent", first: true, predicate: ["detailForm"], descendants: true }], ngImport: i0, template: "<!-- Schedule Drawer -->\n<p-drawer\n [(visible)]=\"visible\"\n position=\"right\"\n [style]=\"{ width: '85vw', 'max-width': '1200px' }\"\n [modal]=\"true\"\n (onHide)=\"onCancel()\"\n>\n <ng-template pTemplate=\"header\">\n <span class=\"drawer-title\">\n <i class=\"pi pi-calendar\"></i>\n {{\n isEditing\n ? ('crmx.components.schedule_schedule_edit' | translate)\n : ('crmx.components.schedule_schedule_new' | translate)\n }}\n </span>\n </ng-template>\n\n <div class=\"drawer-layout\" *ngIf=\"scheduleForm\">\n <!-- Left: Form -->\n <div class=\"drawer-left\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-info-circle\"></i>\n <span>\n {{ 'crmx.components.schedule_schedule_details' | translate }}\n </span>\n </div>\n <sia-dynamic-form\n #detailForm\n [sections]=\"[{ fields: scheduleDetailFields }]\"\n [entityData]=\"entityDataForForm\"\n [mode]=\"'form'\"\n [showSubmitButton]=\"false\"\n [showCancelButton]=\"false\"\n (formReady)=\"onDetailFormReady($event)\"\n ></sia-dynamic-form>\n </div>\n\n <!-- Right: Recurrence + Participants -->\n <div class=\"drawer-right\">\n <div\n class=\"drawer-section\"\n *ngIf=\"showRecurrenceFields\"\n >\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-replay\"></i>\n <span>\n {{\n 'crmx.components.schedule_recurrence_config'\n | translate\n }}\n </span>\n </div>\n <form\n [formGroup]=\"scheduleForm\"\n class=\"drawer-form\"\n >\n <div class=\"form-row\">\n <div class=\"form-field flex-1\">\n <label for=\"recurrence\">\n {{ 'crmx.components.schedule_recurrence' | translate }}\n </label>\n <p-dropdown\n id=\"recurrence\"\n formControlName=\"recurrence\"\n [options]=\"recurrenceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div class=\"form-field flex-1\">\n <label for=\"recurrenceInterval\">\n {{\n 'crmx.components.schedule_recurrence_interval'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"recurrenceInterval\"\n formControlName=\"recurrenceInterval\"\n [min]=\"1\"\n [max]=\"99\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showWeekDays\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"weekDays\">\n {{ 'crmx.components.schedule_week_days' | translate }}\n </label>\n <p-multiSelect\n id=\"weekDays\"\n formControlName=\"weekDays\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"\n 'crmx.components.schedule_select_week_day'\n | translate\n \"\n [maxSelectedLabels]=\"7\"\n class=\"w-full\"\n ></p-multiSelect>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showRepeatWhen\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhen\">\n {{\n 'crmx.components.schedule_repeat_when' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhen\"\n formControlName=\"repeatWhen\"\n [options]=\"repeatWhenOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showRepeatWhenDay\"\n >\n <label for=\"repeatWhenDay\">\n {{\n 'crmx.components.schedule_repeat_when_day'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"repeatWhenDay\"\n formControlName=\"repeatWhenDay\"\n [min]=\"1\"\n [max]=\"31\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenOrdinalWeekDay\">\n {{\n 'crmx.components.schedule_ordinal_week_day'\n | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenOrdinalWeekDay\"\n formControlName=\"repeatWhenOrdinalWeekDay\"\n [options]=\"ordinalOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenWeekMonth\">\n {{\n 'crmx.components.schedule_week_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenWeekMonth\"\n formControlName=\"repeatWhenWeekMonth\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showMonthField\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhenMonth\">\n {{\n 'crmx.components.schedule_repeat_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenMonth\"\n formControlName=\"repeatWhenMonth\"\n [options]=\"monthOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n </form>\n <div\n class=\"recurrence-description\"\n *ngIf=\"recurrenceDescription\"\n >\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ recurrenceDescription }}</span>\n </div>\n </div>\n\n <p-divider></p-divider>\n\n <!-- Participants -->\n <div class=\"drawer-section\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-users\"></i>\n <span>\n {{ 'crmx.components.schedule_participants' | translate }}\n </span>\n </div>\n <div class=\"participant-search\">\n <p-autoComplete\n [(ngModel)]=\"selectedParticipantLookup\"\n [ngModelOptions]=\"{standalone: true}\"\n [suggestions]=\"participantSuggestions\"\n (completeMethod)=\"\n onSearchParticipants($event)\n \"\n (onSelect)=\"\n onSelectParticipant($event.value)\n \"\n (onFocus)=\"\n onSearchParticipants({ query: '' })\n \"\n optionLabel=\"name\"\n [placeholder]=\"\n 'crmx.components.schedule_search_participant'\n | translate\n \"\n [minLength]=\"0\"\n [forceSelection]=\"true\"\n [showClear]=\"true\"\n [fluid]=\"true\"\n >\n <ng-template\n let-item\n pTemplate=\"item\"\n >\n <div class=\"participant-suggestion\">\n <i\n [class]=\"getOriginIcon(item.origin)\"\n ></i>\n <div\n class=\"participant-suggestion-info\"\n >\n <span\n class=\"participant-suggestion-name\"\n >\n {{ item.name }}\n </span>\n <span\n class=\"participant-suggestion-detail\"\n *ngIf=\"item.referenceName\"\n >\n {{ item.referenceName }}\n </span>\n </div>\n <span\n class=\"participant-suggestion-origin\"\n >\n {{ getOriginLabel(item.origin) }}\n </span>\n </div>\n </ng-template>\n </p-autoComplete>\n </div>\n <div\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length === 0\n \"\n class=\"participants-empty\"\n >\n <i class=\"pi pi-users\"></i>\n <p>\n {{\n 'crmx.components.schedule_no_participants'\n | translate\n }}\n </p>\n </div>\n <p-table\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length > 0\n \"\n [value]=\"addedParticipants\"\n styleClass=\"p-datatable-sm p-datatable-striped\"\n >\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">\n {{ 'crmx.components.schedule_name' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-participant-type\"\n >\n {{\n 'crmx.components.schedule_participant_type'\n | translate\n }}\n </th>\n <th\n scope=\"col\"\n class=\"col-actions\"\n ></th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-p>\n <tr>\n <td>\n <div class=\"participant-name-cell\">\n <span>{{ p.name }}</span>\n <span\n *ngIf=\"p.referenceName\"\n class=\"participant-reference\"\n >\n {{ p.referenceName }}\n </span>\n </div>\n </td>\n <td>\n <div class=\"participant-type-cell\">\n <i\n [class]=\"getOriginIcon(p.origin)\"\n ></i>\n <span>\n {{ getOriginLabel(p.origin) }}\n </span>\n </div>\n </td>\n <td>\n <p-button\n icon=\"pi pi-trash\"\n [rounded]=\"true\"\n [text]=\"true\"\n severity=\"danger\"\n (onClick)=\"onRemoveParticipant(p)\"\n [pTooltip]=\"\n 'crmx.components.schedule_remove' | translate\n \"\n tooltipPosition=\"top\"\n ></p-button>\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"drawer-footer\">\n <p-button\n [label]=\"'crmx.components.cancel' | translate\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"onCancel()\"\n ></p-button>\n <p-button\n [label]=\"'crmx.components.save' | translate\"\n icon=\"pi pi-check\"\n (onClick)=\"onSave()\"\n ></p-button>\n </div>\n </ng-template>\n</p-drawer>\n", styles: [".col-participant-type{width:10rem}.col-actions{width:4rem}.w-full{width:100%}.drawer-title{display:flex;align-items:center;gap:.75rem;font-size:1.15rem;font-weight:600;color:var(--p-text-color)}.drawer-title i{color:var(--p-primary-color)}.drawer-layout{display:flex;gap:0;height:100%}.drawer-left{width:380px;min-width:380px;border-right:1px solid var(--p-surface-200);padding:0 1.5rem 1.5rem 0;overflow-y:auto}.drawer-right{flex:1;padding:0 0 0 1.5rem;overflow-y:auto}.drawer-section-header{display:flex;align-items:center;gap:.5rem;padding-bottom:1rem;margin-bottom:1rem;border-bottom:1px solid var(--p-surface-200);font-weight:600;font-size:.95rem;color:var(--p-text-color)}.drawer-section-header i{color:var(--p-primary-color);font-size:1rem}.drawer-form{display:flex;flex-direction:column;gap:1rem}.drawer-form .form-field{display:flex;flex-direction:column;gap:.375rem}.drawer-form .form-field.flex-1{flex:1 1 0%;min-width:0}.drawer-form .form-field label{font-weight:500;font-size:.8rem;color:var(--p-text-secondary-color);text-transform:uppercase;letter-spacing:.3px}.drawer-form .form-row{display:flex;gap:1rem}.drawer-form .form-row.checkboxes{gap:2rem;padding:.5rem 0}.drawer-form .form-check{display:flex;align-items:center;gap:.5rem}.drawer-form .form-check label{font-size:.875rem;font-weight:500;margin:0;text-transform:none;letter-spacing:0}.drawer-section{margin-bottom:1.5rem}.recurrence-description{display:flex;align-items:flex-start;gap:.5rem;margin-top:1rem;padding:.75rem 1rem;background:var(--p-primary-50);border-left:3px solid var(--p-primary-color);border-radius:4px;font-size:.85rem;color:var(--p-text-color);line-height:1.4}.recurrence-description i{color:var(--p-primary-color);margin-top:.1rem;flex-shrink:0}.participants-empty{display:flex;flex-direction:column;align-items:center;padding:2rem;text-align:center;color:var(--p-text-secondary-color);border:1px dashed var(--p-surface-300);border-radius:8px}.participants-empty i{font-size:2rem;margin-bottom:.5rem;opacity:.4}.participants-empty p{margin:0}.participant-name-cell{display:flex;align-items:center;gap:.5rem}.participant-type-cell{display:flex;align-items:center;gap:.5rem;font-size:.85rem;color:var(--p-text-secondary-color)}.participant-type-cell i{font-size:.9rem}.drawer-footer{display:flex;gap:.75rem;justify-content:flex-end}.participant-search{margin-bottom:1rem}.participant-search :host ::ng-deep .p-autocomplete{width:100%;display:flex}.participant-search :host ::ng-deep .p-autocomplete-input{width:100%}.participant-suggestion{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.participant-suggestion i{font-size:1.1rem;color:var(--p-text-secondary-color)}.participant-suggestion-info{display:flex;flex-direction:column;flex:1;min-width:0}.participant-suggestion-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.participant-suggestion-detail{font-size:.8rem;color:var(--p-text-secondary-color)}.participant-suggestion-origin{font-size:.75rem;color:var(--p-text-secondary-color);white-space:nowrap}.participant-reference{font-size:.8rem;color:var(--p-text-secondary-color);margin-left:.25rem}.detail-dialog-body{display:flex;flex-direction:column;gap:.5rem}.detail-dialog-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0}.detail-label{font-weight:500;color:var(--p-text-secondary-color);min-width:8rem}@media (max-width: 768px){.drawer-layout{flex-direction:column}.drawer-left{width:100%;min-width:100%;border-right:none;border-bottom:1px solid var(--p-surface-200);padding:0 0 1.5rem}.drawer-right{padding:1.5rem 0 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i8.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i9.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i10.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i11.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled", "fluid"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i12.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: TagModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i13.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i14.Divider, selector: "p-divider", inputs: ["style", "styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i15.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i16.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "delay", "style", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "disabled", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "maxlength", "name", "required", "size", "appendTo", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "field", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "itemSize", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "variant", "fluid"], outputs: ["completeMethod", "onSelect", "onUnselect", "onFocus", "onBlur", "onDropdownClick", "onClear", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: DynamicFormComponent, selector: "sia-dynamic-form", inputs: ["sections", "entityData", "mode", "displayMode", "visible", "dialogConfig", "drawerConfig", "showSubmitButton", "showCancelButton", "submitButtonLabel", "cancelButtonLabel", "submitButtonIcon", "cancelButtonIcon"], outputs: ["formReady", "formSubmit", "visibleChange", "onCancel", "fieldSave"] }] });
|
|
1939
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScheduleFormComponent, deps: [{ token: i1$3.FormBuilder }, { token: ScheduleService }, { token: ScheduleTypeService }, { token: CurrentCollaboratorService }, { token: i5.TranslationService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1940
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScheduleFormComponent, isStandalone: true, selector: "s-schedule-form", inputs: { visible: "visible", selectedDate: "selectedDate" }, outputs: { visibleChange: "visibleChange", saved: "saved" }, viewQueries: [{ propertyName: "detailFormComponent", first: true, predicate: ["detailForm"], descendants: true }], ngImport: i0, template: "<!-- Schedule Drawer -->\n<p-drawer\n [(visible)]=\"visible\"\n position=\"right\"\n [style]=\"{ width: '85vw', 'max-width': '1200px' }\"\n [modal]=\"true\"\n (onHide)=\"onCancel()\"\n>\n <ng-template pTemplate=\"header\">\n <span class=\"drawer-title\">\n <i class=\"pi pi-calendar\"></i>\n {{\n isEditing\n ? ('crmx.components.schedule_schedule_edit' | translate)\n : ('crmx.components.schedule_schedule_new' | translate)\n }}\n </span>\n </ng-template>\n\n <div class=\"drawer-layout\" *ngIf=\"scheduleForm\">\n <!-- Left: Form -->\n <div class=\"drawer-left\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-info-circle\"></i>\n <span>\n {{ 'crmx.components.schedule_schedule_details' | translate }}\n </span>\n </div>\n <sia-dynamic-form\n #detailForm\n [sections]=\"[{ fields: scheduleDetailFields }]\"\n [entityData]=\"entityDataForForm\"\n [mode]=\"'form'\"\n [showSubmitButton]=\"false\"\n [showCancelButton]=\"false\"\n (formReady)=\"onDetailFormReady($event)\"\n ></sia-dynamic-form>\n </div>\n\n <!-- Right: Recurrence + Participants -->\n <div class=\"drawer-right\">\n <div\n class=\"drawer-section\"\n *ngIf=\"showRecurrenceFields\"\n >\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-replay\"></i>\n <span>\n {{\n 'crmx.components.schedule_recurrence_config'\n | translate\n }}\n </span>\n </div>\n <form\n [formGroup]=\"scheduleForm\"\n class=\"drawer-form\"\n >\n <div class=\"form-row\">\n <div class=\"form-field flex-1\">\n <label for=\"recurrence\">\n {{ 'crmx.components.schedule_recurrence' | translate }}\n </label>\n <p-dropdown\n id=\"recurrence\"\n formControlName=\"recurrence\"\n [options]=\"recurrenceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div class=\"form-field flex-1\">\n <label for=\"recurrenceInterval\">\n {{\n 'crmx.components.schedule_recurrence_interval'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"recurrenceInterval\"\n formControlName=\"recurrenceInterval\"\n [min]=\"1\"\n [max]=\"99\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showWeekDays\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"weekDays\">\n {{ 'crmx.components.schedule_week_days' | translate }}\n </label>\n <p-multiSelect\n id=\"weekDays\"\n formControlName=\"weekDays\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"\n 'crmx.components.schedule_select_week_day'\n | translate\n \"\n [maxSelectedLabels]=\"7\"\n class=\"w-full\"\n ></p-multiSelect>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showRepeatWhen\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhen\">\n {{\n 'crmx.components.schedule_repeat_when' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhen\"\n formControlName=\"repeatWhen\"\n [options]=\"repeatWhenOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showRepeatWhenDay\"\n >\n <label for=\"repeatWhenDay\">\n {{\n 'crmx.components.schedule_repeat_when_day'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"repeatWhenDay\"\n formControlName=\"repeatWhenDay\"\n [min]=\"1\"\n [max]=\"31\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenOrdinalWeekDay\">\n {{\n 'crmx.components.schedule_ordinal_week_day'\n | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenOrdinalWeekDay\"\n formControlName=\"repeatWhenOrdinalWeekDay\"\n [options]=\"ordinalOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenWeekMonth\">\n {{\n 'crmx.components.schedule_week_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenWeekMonth\"\n formControlName=\"repeatWhenWeekMonth\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showMonthField\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhenMonth\">\n {{\n 'crmx.components.schedule_repeat_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenMonth\"\n formControlName=\"repeatWhenMonth\"\n [options]=\"monthOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n </form>\n <div\n class=\"recurrence-description\"\n *ngIf=\"recurrenceDescription\"\n >\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ recurrenceDescription }}</span>\n </div>\n </div>\n\n <p-divider></p-divider>\n\n <!-- Participants -->\n <div class=\"drawer-section\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-users\"></i>\n <span>\n {{ 'crmx.components.schedule_participants' | translate }}\n </span>\n </div>\n <div class=\"participant-search\">\n <p-autoComplete\n [(ngModel)]=\"selectedParticipantLookup\"\n [ngModelOptions]=\"{standalone: true}\"\n [suggestions]=\"participantSuggestions\"\n (completeMethod)=\"\n onSearchParticipants($event)\n \"\n (onSelect)=\"\n onSelectParticipant($event.value)\n \"\n (onFocus)=\"\n onSearchParticipants({ query: '' })\n \"\n optionLabel=\"name\"\n [placeholder]=\"\n 'crmx.components.schedule_search_participant'\n | translate\n \"\n [minLength]=\"0\"\n [forceSelection]=\"true\"\n [showClear]=\"true\"\n [fluid]=\"true\"\n >\n <ng-template\n let-item\n pTemplate=\"item\"\n >\n <div class=\"participant-suggestion\">\n <i\n [class]=\"getOriginIcon(item.origin)\"\n ></i>\n <div\n class=\"participant-suggestion-info\"\n >\n <span\n class=\"participant-suggestion-name\"\n >\n {{ item.name }}\n </span>\n <span\n class=\"participant-suggestion-detail\"\n *ngIf=\"item.referenceName\"\n >\n {{ item.referenceName }}\n </span>\n </div>\n <span\n class=\"participant-suggestion-origin\"\n >\n {{ getOriginLabel(item.origin) }}\n </span>\n </div>\n </ng-template>\n </p-autoComplete>\n </div>\n <div\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length === 0\n \"\n class=\"participants-empty\"\n >\n <i class=\"pi pi-users\"></i>\n <p>\n {{\n 'crmx.components.schedule_no_participants'\n | translate\n }}\n </p>\n </div>\n <p-table\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length > 0\n \"\n [value]=\"addedParticipants\"\n styleClass=\"p-datatable-sm p-datatable-striped\"\n >\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">\n {{ 'crmx.components.schedule_name' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-participant-type\"\n >\n {{\n 'crmx.components.schedule_participant_type'\n | translate\n }}\n </th>\n <th\n scope=\"col\"\n class=\"col-actions\"\n ></th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-p>\n <tr>\n <td>\n <div class=\"participant-name-cell\">\n <span>{{ p.name }}</span>\n <span\n *ngIf=\"p.referenceName\"\n class=\"participant-reference\"\n >\n {{ p.referenceName }}\n </span>\n </div>\n </td>\n <td>\n <div class=\"participant-type-cell\">\n <i\n [class]=\"getOriginIcon(p.origin)\"\n ></i>\n <span>\n {{ getOriginLabel(p.origin) }}\n </span>\n </div>\n </td>\n <td>\n <p-button\n icon=\"pi pi-trash\"\n [rounded]=\"true\"\n [text]=\"true\"\n severity=\"danger\"\n (onClick)=\"onRemoveParticipant(p)\"\n [pTooltip]=\"\n 'crmx.components.schedule_remove' | translate\n \"\n tooltipPosition=\"top\"\n ></p-button>\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"drawer-footer\">\n <p-button\n [label]=\"'crmx.components.cancel' | translate\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"onCancel()\"\n ></p-button>\n <p-button\n [label]=\"'crmx.components.save' | translate\"\n icon=\"pi pi-check\"\n (onClick)=\"onSave()\"\n ></p-button>\n </div>\n </ng-template>\n</p-drawer>\n", styles: [".col-participant-type{width:10rem}.col-actions{width:4rem}.w-full{width:100%}.drawer-title{display:flex;align-items:center;gap:.75rem;font-size:1.15rem;font-weight:600;color:var(--p-text-color)}.drawer-title i{color:var(--p-primary-color)}.drawer-layout{display:flex;gap:0;height:100%}.drawer-left{width:380px;min-width:380px;border-right:1px solid var(--p-surface-200);padding:0 1.5rem 1.5rem 0;overflow-y:auto}.drawer-right{flex:1;padding:0 0 0 1.5rem;overflow-y:auto}.drawer-section-header{display:flex;align-items:center;gap:.5rem;padding-bottom:1rem;margin-bottom:1rem;border-bottom:1px solid var(--p-surface-200);font-weight:600;font-size:.95rem;color:var(--p-text-color)}.drawer-section-header i{color:var(--p-primary-color);font-size:1rem}.drawer-form{display:flex;flex-direction:column;gap:1rem}.drawer-form .form-field{display:flex;flex-direction:column;gap:.375rem}.drawer-form .form-field.flex-1{flex:1 1 0%;min-width:0}.drawer-form .form-field label{font-weight:500;font-size:.8rem;color:var(--p-text-secondary-color);text-transform:uppercase;letter-spacing:.3px}.drawer-form .form-row{display:flex;gap:1rem}.drawer-form .form-row.checkboxes{gap:2rem;padding:.5rem 0}.drawer-form .form-check{display:flex;align-items:center;gap:.5rem}.drawer-form .form-check label{font-size:.875rem;font-weight:500;margin:0;text-transform:none;letter-spacing:0}.drawer-section{margin-bottom:1.5rem}.recurrence-description{display:flex;align-items:flex-start;gap:.5rem;margin-top:1rem;padding:.75rem 1rem;background:var(--p-primary-50);border-left:3px solid var(--p-primary-color);border-radius:4px;font-size:.85rem;color:var(--p-text-color);line-height:1.4}.recurrence-description i{color:var(--p-primary-color);margin-top:.1rem;flex-shrink:0}.participants-empty{display:flex;flex-direction:column;align-items:center;padding:2rem;text-align:center;color:var(--p-text-secondary-color);border:1px dashed var(--p-surface-300);border-radius:8px}.participants-empty i{font-size:2rem;margin-bottom:.5rem;opacity:.4}.participants-empty p{margin:0}.participant-name-cell{display:flex;align-items:center;gap:.5rem}.participant-type-cell{display:flex;align-items:center;gap:.5rem;font-size:.85rem;color:var(--p-text-secondary-color)}.participant-type-cell i{font-size:.9rem}.drawer-footer{display:flex;gap:.75rem;justify-content:flex-end}.participant-search{margin-bottom:1rem}.participant-search :host ::ng-deep .p-autocomplete{width:100%;display:flex}.participant-search :host ::ng-deep .p-autocomplete-input{width:100%}.participant-suggestion{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.participant-suggestion i{font-size:1.1rem;color:var(--p-text-secondary-color)}.participant-suggestion-info{display:flex;flex-direction:column;flex:1;min-width:0}.participant-suggestion-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.participant-suggestion-detail{font-size:.8rem;color:var(--p-text-secondary-color)}.participant-suggestion-origin{font-size:.75rem;color:var(--p-text-secondary-color);white-space:nowrap}.participant-reference{font-size:.8rem;color:var(--p-text-secondary-color);margin-left:.25rem}.detail-dialog-body{display:flex;flex-direction:column;gap:.5rem}.detail-dialog-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0}.detail-label{font-weight:500;color:var(--p-text-secondary-color);min-width:8rem}@media (max-width: 768px){.drawer-layout{flex-direction:column}.drawer-left{width:100%;min-width:100%;border-right:none;border-bottom:1px solid var(--p-surface-200);padding:0 0 1.5rem}.drawer-right{padding:1.5rem 0 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i8.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i9.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i10.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i11.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled", "fluid"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i12.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: TagModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i13.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i14.Divider, selector: "p-divider", inputs: ["style", "styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i15.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i16.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "delay", "style", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "disabled", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "maxlength", "name", "required", "size", "appendTo", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "field", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "itemSize", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "variant", "fluid"], outputs: ["completeMethod", "onSelect", "onUnselect", "onFocus", "onBlur", "onDropdownClick", "onClear", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: DynamicFormComponent, selector: "sia-dynamic-form", inputs: ["sections", "entityData", "mode", "displayMode", "visible", "dialogConfig", "drawerConfig", "showSubmitButton", "showCancelButton", "submitButtonLabel", "cancelButtonLabel", "submitButtonIcon", "cancelButtonIcon"], outputs: ["formReady", "formSubmit", "visibleChange", "onCancel", "fieldSave"] }] });
|
|
1654
1941
|
}
|
|
1655
1942
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScheduleFormComponent, decorators: [{
|
|
1656
1943
|
type: Component,
|
|
@@ -1661,7 +1948,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1661
1948
|
DividerModule, TableModule, AutoCompleteModule,
|
|
1662
1949
|
TranslatePipe, DynamicFormComponent
|
|
1663
1950
|
], template: "<!-- Schedule Drawer -->\n<p-drawer\n [(visible)]=\"visible\"\n position=\"right\"\n [style]=\"{ width: '85vw', 'max-width': '1200px' }\"\n [modal]=\"true\"\n (onHide)=\"onCancel()\"\n>\n <ng-template pTemplate=\"header\">\n <span class=\"drawer-title\">\n <i class=\"pi pi-calendar\"></i>\n {{\n isEditing\n ? ('crmx.components.schedule_schedule_edit' | translate)\n : ('crmx.components.schedule_schedule_new' | translate)\n }}\n </span>\n </ng-template>\n\n <div class=\"drawer-layout\" *ngIf=\"scheduleForm\">\n <!-- Left: Form -->\n <div class=\"drawer-left\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-info-circle\"></i>\n <span>\n {{ 'crmx.components.schedule_schedule_details' | translate }}\n </span>\n </div>\n <sia-dynamic-form\n #detailForm\n [sections]=\"[{ fields: scheduleDetailFields }]\"\n [entityData]=\"entityDataForForm\"\n [mode]=\"'form'\"\n [showSubmitButton]=\"false\"\n [showCancelButton]=\"false\"\n (formReady)=\"onDetailFormReady($event)\"\n ></sia-dynamic-form>\n </div>\n\n <!-- Right: Recurrence + Participants -->\n <div class=\"drawer-right\">\n <div\n class=\"drawer-section\"\n *ngIf=\"showRecurrenceFields\"\n >\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-replay\"></i>\n <span>\n {{\n 'crmx.components.schedule_recurrence_config'\n | translate\n }}\n </span>\n </div>\n <form\n [formGroup]=\"scheduleForm\"\n class=\"drawer-form\"\n >\n <div class=\"form-row\">\n <div class=\"form-field flex-1\">\n <label for=\"recurrence\">\n {{ 'crmx.components.schedule_recurrence' | translate }}\n </label>\n <p-dropdown\n id=\"recurrence\"\n formControlName=\"recurrence\"\n [options]=\"recurrenceOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div class=\"form-field flex-1\">\n <label for=\"recurrenceInterval\">\n {{\n 'crmx.components.schedule_recurrence_interval'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"recurrenceInterval\"\n formControlName=\"recurrenceInterval\"\n [min]=\"1\"\n [max]=\"99\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showWeekDays\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"weekDays\">\n {{ 'crmx.components.schedule_week_days' | translate }}\n </label>\n <p-multiSelect\n id=\"weekDays\"\n formControlName=\"weekDays\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"\n 'crmx.components.schedule_select_week_day'\n | translate\n \"\n [maxSelectedLabels]=\"7\"\n class=\"w-full\"\n ></p-multiSelect>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showRepeatWhen\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhen\">\n {{\n 'crmx.components.schedule_repeat_when' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhen\"\n formControlName=\"repeatWhen\"\n [options]=\"repeatWhenOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showRepeatWhenDay\"\n >\n <label for=\"repeatWhenDay\">\n {{\n 'crmx.components.schedule_repeat_when_day'\n | translate\n }}\n </label>\n <p-inputNumber\n id=\"repeatWhenDay\"\n formControlName=\"repeatWhenDay\"\n [min]=\"1\"\n [max]=\"31\"\n class=\"w-full\"\n ></p-inputNumber>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenOrdinalWeekDay\">\n {{\n 'crmx.components.schedule_ordinal_week_day'\n | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenOrdinalWeekDay\"\n formControlName=\"repeatWhenOrdinalWeekDay\"\n [options]=\"ordinalOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n <div\n class=\"form-field flex-1\"\n *ngIf=\"showOrdinalFields\"\n >\n <label for=\"repeatWhenWeekMonth\">\n {{\n 'crmx.components.schedule_week_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenWeekMonth\"\n formControlName=\"repeatWhenWeekMonth\"\n [options]=\"weekDayOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n <div\n class=\"form-row\"\n *ngIf=\"showMonthField\"\n >\n <div class=\"form-field flex-1\">\n <label for=\"repeatWhenMonth\">\n {{\n 'crmx.components.schedule_repeat_month' | translate\n }}\n </label>\n <p-dropdown\n id=\"repeatWhenMonth\"\n formControlName=\"repeatWhenMonth\"\n [options]=\"monthOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"w-full\"\n ></p-dropdown>\n </div>\n </div>\n </form>\n <div\n class=\"recurrence-description\"\n *ngIf=\"recurrenceDescription\"\n >\n <i class=\"pi pi-info-circle\"></i>\n <span>{{ recurrenceDescription }}</span>\n </div>\n </div>\n\n <p-divider></p-divider>\n\n <!-- Participants -->\n <div class=\"drawer-section\">\n <div class=\"drawer-section-header\">\n <i class=\"pi pi-users\"></i>\n <span>\n {{ 'crmx.components.schedule_participants' | translate }}\n </span>\n </div>\n <div class=\"participant-search\">\n <p-autoComplete\n [(ngModel)]=\"selectedParticipantLookup\"\n [ngModelOptions]=\"{standalone: true}\"\n [suggestions]=\"participantSuggestions\"\n (completeMethod)=\"\n onSearchParticipants($event)\n \"\n (onSelect)=\"\n onSelectParticipant($event.value)\n \"\n (onFocus)=\"\n onSearchParticipants({ query: '' })\n \"\n optionLabel=\"name\"\n [placeholder]=\"\n 'crmx.components.schedule_search_participant'\n | translate\n \"\n [minLength]=\"0\"\n [forceSelection]=\"true\"\n [showClear]=\"true\"\n [fluid]=\"true\"\n >\n <ng-template\n let-item\n pTemplate=\"item\"\n >\n <div class=\"participant-suggestion\">\n <i\n [class]=\"getOriginIcon(item.origin)\"\n ></i>\n <div\n class=\"participant-suggestion-info\"\n >\n <span\n class=\"participant-suggestion-name\"\n >\n {{ item.name }}\n </span>\n <span\n class=\"participant-suggestion-detail\"\n *ngIf=\"item.referenceName\"\n >\n {{ item.referenceName }}\n </span>\n </div>\n <span\n class=\"participant-suggestion-origin\"\n >\n {{ getOriginLabel(item.origin) }}\n </span>\n </div>\n </ng-template>\n </p-autoComplete>\n </div>\n <div\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length === 0\n \"\n class=\"participants-empty\"\n >\n <i class=\"pi pi-users\"></i>\n <p>\n {{\n 'crmx.components.schedule_no_participants'\n | translate\n }}\n </p>\n </div>\n <p-table\n *ngIf=\"\n !participantsLoading &&\n addedParticipants.length > 0\n \"\n [value]=\"addedParticipants\"\n styleClass=\"p-datatable-sm p-datatable-striped\"\n >\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">\n {{ 'crmx.components.schedule_name' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-participant-type\"\n >\n {{\n 'crmx.components.schedule_participant_type'\n | translate\n }}\n </th>\n <th\n scope=\"col\"\n class=\"col-actions\"\n ></th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-p>\n <tr>\n <td>\n <div class=\"participant-name-cell\">\n <span>{{ p.name }}</span>\n <span\n *ngIf=\"p.referenceName\"\n class=\"participant-reference\"\n >\n {{ p.referenceName }}\n </span>\n </div>\n </td>\n <td>\n <div class=\"participant-type-cell\">\n <i\n [class]=\"getOriginIcon(p.origin)\"\n ></i>\n <span>\n {{ getOriginLabel(p.origin) }}\n </span>\n </div>\n </td>\n <td>\n <p-button\n icon=\"pi pi-trash\"\n [rounded]=\"true\"\n [text]=\"true\"\n severity=\"danger\"\n (onClick)=\"onRemoveParticipant(p)\"\n [pTooltip]=\"\n 'crmx.components.schedule_remove' | translate\n \"\n tooltipPosition=\"top\"\n ></p-button>\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"drawer-footer\">\n <p-button\n [label]=\"'crmx.components.cancel' | translate\"\n icon=\"pi pi-times\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"onCancel()\"\n ></p-button>\n <p-button\n [label]=\"'crmx.components.save' | translate\"\n icon=\"pi pi-check\"\n (onClick)=\"onSave()\"\n ></p-button>\n </div>\n </ng-template>\n</p-drawer>\n", styles: [".col-participant-type{width:10rem}.col-actions{width:4rem}.w-full{width:100%}.drawer-title{display:flex;align-items:center;gap:.75rem;font-size:1.15rem;font-weight:600;color:var(--p-text-color)}.drawer-title i{color:var(--p-primary-color)}.drawer-layout{display:flex;gap:0;height:100%}.drawer-left{width:380px;min-width:380px;border-right:1px solid var(--p-surface-200);padding:0 1.5rem 1.5rem 0;overflow-y:auto}.drawer-right{flex:1;padding:0 0 0 1.5rem;overflow-y:auto}.drawer-section-header{display:flex;align-items:center;gap:.5rem;padding-bottom:1rem;margin-bottom:1rem;border-bottom:1px solid var(--p-surface-200);font-weight:600;font-size:.95rem;color:var(--p-text-color)}.drawer-section-header i{color:var(--p-primary-color);font-size:1rem}.drawer-form{display:flex;flex-direction:column;gap:1rem}.drawer-form .form-field{display:flex;flex-direction:column;gap:.375rem}.drawer-form .form-field.flex-1{flex:1 1 0%;min-width:0}.drawer-form .form-field label{font-weight:500;font-size:.8rem;color:var(--p-text-secondary-color);text-transform:uppercase;letter-spacing:.3px}.drawer-form .form-row{display:flex;gap:1rem}.drawer-form .form-row.checkboxes{gap:2rem;padding:.5rem 0}.drawer-form .form-check{display:flex;align-items:center;gap:.5rem}.drawer-form .form-check label{font-size:.875rem;font-weight:500;margin:0;text-transform:none;letter-spacing:0}.drawer-section{margin-bottom:1.5rem}.recurrence-description{display:flex;align-items:flex-start;gap:.5rem;margin-top:1rem;padding:.75rem 1rem;background:var(--p-primary-50);border-left:3px solid var(--p-primary-color);border-radius:4px;font-size:.85rem;color:var(--p-text-color);line-height:1.4}.recurrence-description i{color:var(--p-primary-color);margin-top:.1rem;flex-shrink:0}.participants-empty{display:flex;flex-direction:column;align-items:center;padding:2rem;text-align:center;color:var(--p-text-secondary-color);border:1px dashed var(--p-surface-300);border-radius:8px}.participants-empty i{font-size:2rem;margin-bottom:.5rem;opacity:.4}.participants-empty p{margin:0}.participant-name-cell{display:flex;align-items:center;gap:.5rem}.participant-type-cell{display:flex;align-items:center;gap:.5rem;font-size:.85rem;color:var(--p-text-secondary-color)}.participant-type-cell i{font-size:.9rem}.drawer-footer{display:flex;gap:.75rem;justify-content:flex-end}.participant-search{margin-bottom:1rem}.participant-search :host ::ng-deep .p-autocomplete{width:100%;display:flex}.participant-search :host ::ng-deep .p-autocomplete-input{width:100%}.participant-suggestion{display:flex;align-items:center;gap:.75rem;padding:.25rem 0}.participant-suggestion i{font-size:1.1rem;color:var(--p-text-secondary-color)}.participant-suggestion-info{display:flex;flex-direction:column;flex:1;min-width:0}.participant-suggestion-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.participant-suggestion-detail{font-size:.8rem;color:var(--p-text-secondary-color)}.participant-suggestion-origin{font-size:.75rem;color:var(--p-text-secondary-color);white-space:nowrap}.participant-reference{font-size:.8rem;color:var(--p-text-secondary-color);margin-left:.25rem}.detail-dialog-body{display:flex;flex-direction:column;gap:.5rem}.detail-dialog-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0}.detail-label{font-weight:500;color:var(--p-text-secondary-color);min-width:8rem}@media (max-width: 768px){.drawer-layout{flex-direction:column}.drawer-left{width:100%;min-width:100%;border-right:none;border-bottom:1px solid var(--p-surface-200);padding:0 0 1.5rem}.drawer-right{padding:1.5rem 0 0}}\n"] }]
|
|
1664
|
-
}], ctorParameters: () => [{ type: i1$
|
|
1951
|
+
}], ctorParameters: () => [{ type: i1$3.FormBuilder }, { type: ScheduleService }, { type: ScheduleTypeService }, { type: CurrentCollaboratorService }, { type: i5.TranslationService }], propDecorators: { visible: [{
|
|
1665
1952
|
type: Input
|
|
1666
1953
|
}], selectedDate: [{
|
|
1667
1954
|
type: Input
|
|
@@ -1725,7 +2012,7 @@ class ScheduleDetailComponent {
|
|
|
1725
2012
|
getConfirmationSeverity(c) { return getConfirmationSeverity(c); }
|
|
1726
2013
|
getConfirmationLabel(c) { return getConfirmationLabel(this.t, c); }
|
|
1727
2014
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScheduleDetailComponent, deps: [{ token: ScheduleService }, { token: i5.TranslationService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1728
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScheduleDetailComponent, isStandalone: true, selector: "s-schedule-detail", inputs: { visible: "visible" }, outputs: { visibleChange: "visibleChange" }, ngImport: i0, template: "<p-dialog\n [(visible)]=\"visible\"\n [header]=\"schedule?.name || ''\"\n [modal]=\"true\"\n styleClass=\"detail-dialog\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n (onHide)=\"onClose()\"\n>\n <div class=\"detail-dialog-body\" *ngIf=\"schedule\">\n <div\n class=\"detail-dialog-section\"\n *ngIf=\"scheduleData\"\n >\n <div class=\"detail-dialog-row\">\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_priority' | translate }}\n </span>\n <p-tag\n [value]=\"getPriorityLabel(schedule.priority)\"\n [severity]=\"getPrioritySeverity(schedule.priority)\"\n ></p-tag>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"schedule.scheduleType\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_type' | translate }}\n </span>\n <span>{{ schedule.scheduleType.name }}</span>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"schedule.whenOccurs\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_recurrence' | translate }}\n </span>\n <span>{{ schedule.whenOccurs }}</span>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"scheduleData.comments\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_comments' | translate }}\n </span>\n <span>{{ scheduleData.comments }}</span>\n </div>\n </div>\n <p-divider *ngIf=\"scheduleData\"></p-divider>\n <div\n class=\"detail-dialog-section\"\n *ngIf=\"scheduleData\"\n >\n <div class=\"detail-section-header\">\n <i class=\"pi pi-users\"></i>\n <span>\n {{ 'crmx.components.schedule_participants' | translate }}\n </span>\n </div>\n <p-table\n [value]=\"participants\"\n [loading]=\"participantsLoading\"\n styleClass=\"p-datatable-sm p-datatable-striped\"\n >\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">\n {{ 'crmx.components.schedule_name' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-participant-type\"\n >\n {{ 'crmx.components.schedule_participant_type' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-status\"\n >\n {{ 'crmx.components.schedule_status' | translate }}\n </th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-p>\n <tr>\n <td>\n <span>{{ getParticipantName(p) }}</span>\n </td>\n <td>\n <div class=\"participant-type-cell\">\n <i\n [class]=\"getParticipantTypeIcon(p)\"\n ></i>\n <span>\n {{ getParticipantType(p) }}\n </span>\n </div>\n </td>\n <td>\n <p-tag\n *ngIf=\"p.organizer\"\n [value]=\"\n 'crmx.components.schedule_participant_organizer_label'\n | translate\n \"\n severity=\"info\"\n ></p-tag>\n <p-tag\n *ngIf=\"!p.organizer\"\n [value]=\"\n getConfirmationLabel(p.confirmation)\n \"\n [severity]=\"\n getConfirmationSeverity(p.confirmation)\n \"\n ></p-tag>\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td colspan=\"3\">\n <div class=\"participants-empty\">\n <i class=\"pi pi-users\"></i>\n <p>\n {{\n 'crmx.components.schedule_no_participants'\n | translate\n }}\n </p>\n </div>\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n</p-dialog>\n", styles: [":host ::ng-deep .detail-dialog{width:600px}.detail-dialog-body{display:flex;flex-direction:column;gap:.5rem}.detail-dialog-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0}.detail-label{font-weight:500;color:var(--p-text-secondary-color);min-width:8rem}.detail-section-header{display:flex;align-items:center;gap:.5rem;padding-bottom:1rem;margin-bottom:1rem;border-bottom:1px solid var(--p-surface-200);font-weight:600;font-size:.95rem;color:var(--p-text-color)}.detail-section-header i{color:var(--p-primary-color);font-size:1rem}.participant-type-cell{display:flex;align-items:center;gap:.5rem;font-size:.85rem;color:var(--p-text-secondary-color)}.participant-type-cell i{font-size:.9rem}.col-participant-type{width:10rem}.col-status{width:8rem}.participants-empty{display:flex;flex-direction:column;align-items:center;padding:2rem;text-align:center;color:var(--p-text-secondary-color);border:1px dashed var(--p-surface-300);border-radius:8px}.participants-empty i{font-size:2rem;margin-bottom:.5rem;opacity:.4}.participants-empty p{margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type:
|
|
2015
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScheduleDetailComponent, isStandalone: true, selector: "s-schedule-detail", inputs: { visible: "visible" }, outputs: { visibleChange: "visibleChange" }, ngImport: i0, template: "<p-dialog\n [(visible)]=\"visible\"\n [header]=\"schedule?.name || ''\"\n [modal]=\"true\"\n styleClass=\"detail-dialog\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n (onHide)=\"onClose()\"\n>\n <div class=\"detail-dialog-body\" *ngIf=\"schedule\">\n <div\n class=\"detail-dialog-section\"\n *ngIf=\"scheduleData\"\n >\n <div class=\"detail-dialog-row\">\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_priority' | translate }}\n </span>\n <p-tag\n [value]=\"getPriorityLabel(schedule.priority)\"\n [severity]=\"getPrioritySeverity(schedule.priority)\"\n ></p-tag>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"schedule.scheduleType\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_type' | translate }}\n </span>\n <span>{{ schedule.scheduleType.name }}</span>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"schedule.whenOccurs\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_recurrence' | translate }}\n </span>\n <span>{{ schedule.whenOccurs }}</span>\n </div>\n <div\n class=\"detail-dialog-row\"\n *ngIf=\"scheduleData.comments\"\n >\n <span class=\"detail-label\">\n {{ 'crmx.components.schedule_comments' | translate }}\n </span>\n <span>{{ scheduleData.comments }}</span>\n </div>\n </div>\n <p-divider *ngIf=\"scheduleData\"></p-divider>\n <div\n class=\"detail-dialog-section\"\n *ngIf=\"scheduleData\"\n >\n <div class=\"detail-section-header\">\n <i class=\"pi pi-users\"></i>\n <span>\n {{ 'crmx.components.schedule_participants' | translate }}\n </span>\n </div>\n <p-table\n [value]=\"participants\"\n [loading]=\"participantsLoading\"\n styleClass=\"p-datatable-sm p-datatable-striped\"\n >\n <ng-template pTemplate=\"header\">\n <tr>\n <th scope=\"col\">\n {{ 'crmx.components.schedule_name' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-participant-type\"\n >\n {{ 'crmx.components.schedule_participant_type' | translate }}\n </th>\n <th\n scope=\"col\"\n class=\"col-status\"\n >\n {{ 'crmx.components.schedule_status' | translate }}\n </th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-p>\n <tr>\n <td>\n <span>{{ getParticipantName(p) }}</span>\n </td>\n <td>\n <div class=\"participant-type-cell\">\n <i\n [class]=\"getParticipantTypeIcon(p)\"\n ></i>\n <span>\n {{ getParticipantType(p) }}\n </span>\n </div>\n </td>\n <td>\n <p-tag\n *ngIf=\"p.organizer\"\n [value]=\"\n 'crmx.components.schedule_participant_organizer_label'\n | translate\n \"\n severity=\"info\"\n ></p-tag>\n <p-tag\n *ngIf=\"!p.organizer\"\n [value]=\"\n getConfirmationLabel(p.confirmation)\n \"\n [severity]=\"\n getConfirmationSeverity(p.confirmation)\n \"\n ></p-tag>\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td colspan=\"3\">\n <div class=\"participants-empty\">\n <i class=\"pi pi-users\"></i>\n <p>\n {{\n 'crmx.components.schedule_no_participants'\n | translate\n }}\n </p>\n </div>\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n</p-dialog>\n", styles: [":host ::ng-deep .detail-dialog{width:600px}.detail-dialog-body{display:flex;flex-direction:column;gap:.5rem}.detail-dialog-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0}.detail-label{font-weight:500;color:var(--p-text-secondary-color);min-width:8rem}.detail-section-header{display:flex;align-items:center;gap:.5rem;padding-bottom:1rem;margin-bottom:1rem;border-bottom:1px solid var(--p-surface-200);font-weight:600;font-size:.95rem;color:var(--p-text-color)}.detail-section-header i{color:var(--p-primary-color);font-size:1rem}.participant-type-cell{display:flex;align-items:center;gap:.5rem;font-size:.85rem;color:var(--p-text-secondary-color)}.participant-type-cell i{font-size:.9rem}.col-participant-type{width:10rem}.col-status{width:8rem}.participants-empty{display:flex;flex-direction:column;align-items:center;padding:2rem;text-align:center;color:var(--p-text-secondary-color);border:1px dashed var(--p-surface-300);border-radius:8px}.participants-empty i{font-size:2rem;margin-bottom:.5rem;opacity:.4}.participants-empty p{margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i4.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i8.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i14.Divider, selector: "p-divider", inputs: ["style", "styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i7.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i15.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1729
2016
|
}
|
|
1730
2017
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScheduleDetailComponent, decorators: [{
|
|
1731
2018
|
type: Component,
|
|
@@ -1744,5 +2031,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
1744
2031
|
* Generated bundle index. Do not edit.
|
|
1745
2032
|
*/
|
|
1746
2033
|
|
|
1747
|
-
export { AngularComponentsModule, CurrentCollaboratorService, Month, OrdinalWeekDay, ParticipantConfirmation, RepeatWhen, ScheduleDetailComponent, ScheduleFormComponent, SchedulePriority, ScheduleRecurrence, ScheduleService, ScheduleTypeService, Status, StorageService, WeekDay, provideAngularComponentsTranslations };
|
|
2034
|
+
export { AngularComponentsModule, ChatbotComponent, CurrentCollaboratorService, Month, OrdinalWeekDay, ParticipantConfirmation, RepeatWhen, ScheduleDetailComponent, ScheduleFormComponent, SchedulePriority, ScheduleRecurrence, ScheduleService, ScheduleTypeService, Status, StorageService, WeekDay, provideAngularComponentsTranslations };
|
|
1748
2035
|
//# sourceMappingURL=senior-gestao-relacionamento-angular-components.mjs.map
|