@dataclouder/ngx-lessons 0.1.4 → 0.1.6

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.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Pipe, InjectionToken, inject, Input, Component, ChangeDetectionStrategy, signal, EventEmitter, Output, Injectable, effect, ViewChildren, input, Renderer2, ViewContainerRef, computed, ViewChild, ChangeDetectorRef, output } from '@angular/core';
2
+ import { Pipe, InjectionToken, inject, Input, Component, ChangeDetectionStrategy, signal, EventEmitter, Output, Injectable, effect, ViewChildren, input, viewChild, Renderer2, ViewContainerRef, computed, ChangeDetectorRef, output } from '@angular/core';
3
3
  import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule, SlicePipe } from '@angular/common';
4
- import * as i1$3 from '@angular/router';
5
- import { RouterModule, ActivatedRoute, Router } from '@angular/router';
6
- import { EntityCommunicationService, EntityBaseListComponent, DCFilterBarComponent, QuickTableComponent, TOAST_ALERTS_TOKEN, UiStateService, EModelQuality, LoadingBarService, PromptService, DcExtensionsViewerComponent, DcLearnableViewerComponent, DcAuditableViewerComponent, DcManageableViewerComponent, DcReactionsViewerComponent, HttpCoreService, PaginationBase, EntityBaseFormComponent, getSupportedLanguageOptions } from '@dataclouder/ngx-core';
4
+ import * as i1$4 from '@angular/router';
5
+ import { RouterModule, ActivatedRoute, RouterOutlet, RouterLink } from '@angular/router';
6
+ import { EntityCommunicationService, EntityBaseListComponent, DCFilterBarComponent, QuickTableComponent, TOAST_ALERTS_TOKEN, EModelQuality, UiStateService, LoadingBarService, LangDescTranslation, FormUtilsService, EntityBaseFormComponent, PromptService, DcExtensionsViewerComponent, DcLearnableViewerComponent, DcAuditableViewerComponent, DcReactionsViewerComponent, DcManageableFormComponent, DcLearnableFormComponent, HttpCoreService, PaginationBase, getSupportedLanguageOptions } from '@dataclouder/ngx-core';
7
7
  import * as i1 from '@angular/forms';
8
8
  import { FormBuilder, FormControl, FormArray, FormsModule, ReactiveFormsModule, UntypedFormControl, FormGroup, Validators } from '@angular/forms';
9
9
  import * as i1$1 from 'primeng/button';
@@ -22,24 +22,25 @@ import * as i4$1 from 'primeng/tag';
22
22
  import { TagModule } from 'primeng/tag';
23
23
  import * as i2$1 from 'primeng/speeddial';
24
24
  import { SpeedDialModule } from 'primeng/speeddial';
25
- import * as i3$1 from 'primeng/card';
25
+ import * as i1$2 from 'primeng/card';
26
26
  import { CardModule } from 'primeng/card';
27
- import * as i1$2 from 'primeng/paginator';
27
+ import { UserService } from '@dataclouder/ngx-users';
28
+ import * as i1$3 from 'primeng/paginator';
28
29
  import { PaginatorModule } from 'primeng/paginator';
29
- import { toSignal } from '@angular/core/rxjs-interop';
30
- import { map } from 'rxjs';
31
30
  import BalloonEditor from '@ckeditor/ckeditor5-build-balloon-block';
32
- import * as i3$2 from '@ckeditor/ckeditor5-angular';
31
+ import * as i3$1 from '@ckeditor/ckeditor5-angular';
33
32
  import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
34
- import * as i5$1 from 'primeng/splitter';
33
+ import * as i5$1 from 'primeng/selectbutton';
34
+ import { SelectButtonModule } from 'primeng/selectbutton';
35
+ import * as i6$1 from 'primeng/splitter';
35
36
  import { SplitterModule } from 'primeng/splitter';
36
- import * as i2$3 from 'primeng/tooltip';
37
+ import * as i7 from 'primeng/tooltip';
37
38
  import { TooltipModule } from 'primeng/tooltip';
38
39
  import { ResolutionType, AspectType, AssetsLoaderComponent, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
39
40
  import { TextEngines, ConversationType, USER_DATA_EXCHANGE, SystemPromptType, ChatRole, EvalResultStringDefinition, EDoActionType, ConditionOperator, ConditionType, ConversationEvents, ChatEventType, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
40
41
  import * as i2$2 from 'primeng/drawer';
41
42
  import { DrawerModule } from 'primeng/drawer';
42
- import * as i7 from 'primeng/dialog';
43
+ import { MarkdownComponent, MarkdownService } from 'ngx-markdown';
43
44
  import { DialogModule } from 'primeng/dialog';
44
45
  import TurndownService from 'turndown';
45
46
  import { marked } from 'marked';
@@ -47,8 +48,14 @@ import * as i5 from 'primeng/inputgroup';
47
48
  import { InputGroupModule } from 'primeng/inputgroup';
48
49
  import * as i6 from 'primeng/divider';
49
50
  import { DividerModule } from 'primeng/divider';
50
- import * as i2$4 from 'primeng/api';
51
+ import * as i2$3 from 'primeng/api';
52
+ import { MessageService } from 'primeng/api';
51
53
  import { TableModule } from 'primeng/table';
54
+ import * as i3$2 from 'primeng/panel';
55
+ import { PanelModule } from 'primeng/panel';
56
+ import * as i5$2 from 'primeng/progressspinner';
57
+ import { ProgressSpinnerModule } from 'primeng/progressspinner';
58
+ import { ToastModule } from 'primeng/toast';
52
59
  import * as i3$3 from 'primeng/textarea';
53
60
  import { TextareaModule } from 'primeng/textarea';
54
61
  import { ChipModule } from 'primeng/chip';
@@ -71,25 +78,6 @@ const LangCodeDescriptionEs = {
71
78
  fr: 'Frances',
72
79
  ja: 'Japonés',
73
80
  };
74
- class LangDescTranslationPipe {
75
- transform(value, lang) {
76
- if (lang === 'es') {
77
- return LangCodeDescriptionEs[value];
78
- }
79
- else {
80
- return LangCodeDescription[value];
81
- }
82
- }
83
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LangDescTranslationPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
84
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.6", ngImport: i0, type: LangDescTranslationPipe, isStandalone: true, name: "langDesc" }); }
85
- }
86
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LangDescTranslationPipe, decorators: [{
87
- type: Pipe,
88
- args: [{
89
- name: 'langDesc',
90
- standalone: true,
91
- }]
92
- }] });
93
81
  class FlagLanguagePipe {
94
82
  transform(lang) {
95
83
  if (lang === 'en') {
@@ -627,6 +615,7 @@ var EventCard;
627
615
  })(EventCard || (EventCard = {}));
628
616
  class DcLessonCardComponent {
629
617
  constructor() {
618
+ this.userService = inject(UserService);
630
619
  this.showOptions = true;
631
620
  this.cardHeight = '200px';
632
621
  this.onAction = new EventEmitter();
@@ -639,18 +628,18 @@ class DcLessonCardComponent {
639
628
  ];
640
629
  }
641
630
  ngOnInit() {
642
- this.coverUrl =
643
- this.lesson?.banner?.url || this.lesson?.metadata?.banner?.url || this.lesson?.media?.images?.[0]?.url || 'assets/background/default-background.webp';
631
+ this.coverUrl = this.lesson?.assets?.banner?.url || this.lesson?.media?.images?.[0]?.url || 'assets/background/default-background.webp';
632
+ console.log(this.lesson);
644
633
  }
645
634
  eventCard(eventType) {
646
635
  this.onAction.emit({ action: eventType, item: this.lesson });
647
636
  }
648
637
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
649
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions", cardHeight: "cardHeight" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\n @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if (!lesson.metadata?.isPublished) {\n <p-tag severity=\"danger\" value=\"No publicada\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "pipe", type: DatePipe, name: "date" }] }); }
638
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions", cardHeight: "cardHeight" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n @if (lesson.learnable?.level){\n <p-tag>Nivel {{ lesson.learnable?.level }}</p-tag>\n } @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if ( userService.isAdmin()) {\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.isPublic ? 'P\u00FAblica' : 'No p\u00FAblica'\" [rounded]=\"true\" />\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.status\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "pipe", type: DatePipe, name: "date" }] }); }
650
639
  }
651
640
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DcLessonCardComponent, decorators: [{
652
641
  type: Component,
653
- args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\n @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if (!lesson.metadata?.isPublished) {\n <p-tag severity=\"danger\" value=\"No publicada\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"] }]
642
+ args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n @if (lesson.learnable?.level){\n <p-tag>Nivel {{ lesson.learnable?.level }}</p-tag>\n } @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if ( userService.isAdmin()) {\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.isPublic ? 'P\u00FAblica' : 'No p\u00FAblica'\" [rounded]=\"true\" />\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.status\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"] }]
654
643
  }], propDecorators: { lesson: [{
655
644
  type: Input
656
645
  }], showOptions: [{
@@ -679,12 +668,16 @@ class DefaultLessonsService extends EntityCommunicationService {
679
668
  filters.returnProps = {
680
669
  level: 1,
681
670
  title: 1,
671
+ name: 1,
682
672
  description: 1,
683
673
  createdDate: 1,
684
- banner: 1,
685
- metadata: 1,
674
+ banner: 1, // deprecated
675
+ metadata: 1, // deprecated
676
+ manageable: 1,
677
+ assets: 1,
678
+ learnable: 1,
686
679
  appExtensions: 1,
687
- media: 1,
680
+ media: 1, // deprecated
688
681
  };
689
682
  }
690
683
  return this.query(filters);
@@ -774,6 +767,9 @@ class DCLessonListComponent extends EntityBaseListComponent {
774
767
  title: 1,
775
768
  description: 1,
776
769
  media: 1,
770
+ manageable: 1,
771
+ learnable: 1,
772
+ assets: 1,
777
773
  _id: 1,
778
774
  id: 1,
779
775
  };
@@ -845,11 +841,11 @@ class DCLessonListComponent extends EntityBaseListComponent {
845
841
  }
846
842
  }
847
843
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
848
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { customCardComponent: "customCardComponent", customFilters: "customFilters" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$2.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }] }); }
844
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { customCardComponent: "customCardComponent", customFilters: "customFilters" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n class=\"paginator-container\"\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}.paginator-container{background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }] }); }
849
845
  }
850
846
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonListComponent, decorators: [{
851
847
  type: Component,
852
- args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, NgComponentOutlet, QuickTableComponent, PaginatorModule], template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"] }]
848
+ args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, NgComponentOutlet, QuickTableComponent, PaginatorModule], template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n class=\"paginator-container\"\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}.paginator-container{background:transparent}\n"] }]
853
849
  }], ctorParameters: () => [], propDecorators: { customCardComponent: [{
854
850
  type: Input
855
851
  }], customFilters: [{
@@ -868,6 +864,216 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
868
864
  args: [{ selector: 'dc-lesson-form', standalone: true, imports: [ReactiveFormsModule], template: "<div class=\"lesson-form-container\">\n <h2>Lesson Form</h2>\n <p>Esto es dentro del componente de la leccion</p>\n <!-- Form implementation will go here -->\n</div>\n", styles: [".lesson-form-container{padding:1rem}\n"] }]
869
865
  }] });
870
866
 
867
+ // TODO: check LessonComponentBuilders in lessons.clases.ts for origianl implementation
868
+ const DynamicComponentBuilders = {
869
+ [LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
870
+ };
871
+ const DynamicComponents = {
872
+ [LessonComponentEnum.Speaker]: SpeakerComponent,
873
+ };
874
+ class DynamicComponentsService {
875
+ constructor() {
876
+ this._dynamicComponentBuilders = { ...DynamicComponentBuilders };
877
+ this._dynamicComponents = { ...DynamicComponents };
878
+ }
879
+ registerCustomComponent(component, name) {
880
+ // this._dynamicComponentBuilders[name || component.name] = component;
881
+ this._dynamicComponents[name || component.name] = component;
882
+ }
883
+ registerCustomComponentBuilder(component, name) {
884
+ this._dynamicComponentBuilders[name || component.name] = component;
885
+ }
886
+ getDynamicComponentBuilders(type) {
887
+ return this._dynamicComponentBuilders[type];
888
+ }
889
+ getDynamicComponentClass(type) {
890
+ return this._dynamicComponents[type];
891
+ }
892
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
893
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, providedIn: 'root' }); }
894
+ }
895
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, decorators: [{
896
+ type: Injectable,
897
+ args: [{
898
+ providedIn: 'root',
899
+ }]
900
+ }] });
901
+
902
+ class LessonRendererService {
903
+ constructor() {
904
+ this.dynamicComponentsService = inject(DynamicComponentsService);
905
+ this.toastrService = inject(TOAST_ALERTS_TOKEN);
906
+ this.components = {};
907
+ this.mainForm = new FormGroup({});
908
+ }
909
+ renderLesson(lessonData, viewContainerRef, dynamicLessonElement, renderer) {
910
+ this.clearLessonRendering(dynamicLessonElement);
911
+ console.log('Rendering lesson:', lessonData.id);
912
+ const { htmlContent, components } = this.parseAndCreateComponents(lessonData, viewContainerRef);
913
+ this.components = components;
914
+ this.aggregateFormControls(this.components);
915
+ dynamicLessonElement.innerHTML = htmlContent;
916
+ this.injectComponentsIntoDom(this.components, renderer);
917
+ return this.components;
918
+ }
919
+ clearLessonRendering(dynamicLessonElement) {
920
+ Object.values(this.components).forEach((compRef) => compRef.destroy());
921
+ this.components = {};
922
+ this.mainForm = new FormGroup({});
923
+ if (dynamicLessonElement) {
924
+ dynamicLessonElement.innerHTML = '';
925
+ }
926
+ }
927
+ parseAndCreateComponents(lessonData, viewContainerRef) {
928
+ const r1 = new RegExp('~(.+?)~', 'g');
929
+ let count = 0;
930
+ const createdComponents = {};
931
+ const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
932
+ const componentName = `dynamicComp${count}`;
933
+ count++;
934
+ const componentRef = this.createComponentReferenceWithJson(jsonCoded, lessonData, viewContainerRef);
935
+ if (!componentRef) {
936
+ console.error(`Failed to create component for: ${jsonCoded}`);
937
+ return '<!-- component creation failed -->';
938
+ }
939
+ createdComponents[componentName] = componentRef;
940
+ return `<span id="${componentName}"></span>`;
941
+ });
942
+ return { htmlContent, components: createdComponents };
943
+ }
944
+ createComponentReferenceWithJson(json, currentLesson, viewContainerRef) {
945
+ try {
946
+ let lessonCodedConfig = JSON.parse(json);
947
+ if (lessonCodedConfig.id && currentLesson?.dynamicComponents[lessonCodedConfig.id]) {
948
+ const foundConfig = currentLesson.dynamicComponents[lessonCodedConfig.id];
949
+ if (foundConfig) {
950
+ lessonCodedConfig = { ...lessonCodedConfig, ...foundConfig };
951
+ }
952
+ }
953
+ const LessonClass = this.dynamicComponentsService.getDynamicComponentClass(lessonCodedConfig.component);
954
+ if (!LessonClass) {
955
+ console.error(`Component class not found for type: ${lessonCodedConfig.component}. JSON: ${json}`);
956
+ return null;
957
+ }
958
+ const componentRef = viewContainerRef.createComponent(LessonClass);
959
+ if (lessonCodedConfig.inputs) {
960
+ for (const key in lessonCodedConfig.inputs) {
961
+ if (lessonCodedConfig.inputs.hasOwnProperty(key)) {
962
+ componentRef.instance[key] = lessonCodedConfig.inputs[key];
963
+ }
964
+ }
965
+ }
966
+ if (lessonCodedConfig.settings) {
967
+ componentRef.instance.config = lessonCodedConfig;
968
+ }
969
+ return componentRef;
970
+ }
971
+ catch (error) {
972
+ console.error(`Error processing component JSON: ${json}`, error);
973
+ return null;
974
+ }
975
+ }
976
+ aggregateFormControls(components) {
977
+ const newFormControls = {};
978
+ Object.entries(components).forEach(([name, componentRef]) => {
979
+ if (componentRef.instance?.control instanceof FormControl) {
980
+ newFormControls[name] = componentRef.instance.control;
981
+ newFormControls[name].addValidators(Validators.required);
982
+ }
983
+ });
984
+ this.mainForm = new FormGroup(newFormControls);
985
+ }
986
+ injectComponentsIntoDom(components, renderer) {
987
+ Object.entries(components).forEach(([name, componentRef]) => {
988
+ const elementRef = document.getElementById(name);
989
+ if (elementRef) {
990
+ this.addComponentToNode(componentRef, elementRef, renderer);
991
+ }
992
+ else {
993
+ console.warn(`Placeholder element with ID '${name}' not found in the DOM.`);
994
+ }
995
+ });
996
+ }
997
+ addComponentToNode(componentRef, nodeDOM, renderer) {
998
+ renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
999
+ }
1000
+ evaluateForms() {
1001
+ this.mainForm.markAllAsTouched();
1002
+ if (!this.mainForm.valid) {
1003
+ Object.keys(this.mainForm.controls).forEach((controlName) => {
1004
+ if (this.components[controlName]?.instance?.validate) {
1005
+ this.components[controlName].instance.validate();
1006
+ }
1007
+ });
1008
+ this.toastrService.warn({ subtitle: 'Por favor completa todos los ejercicios', title: 'Incompleto' });
1009
+ return null;
1010
+ }
1011
+ const rates = { correct: 0, incorrect: 0, score: 0 };
1012
+ Object.keys(this.mainForm.controls).forEach((controlName) => {
1013
+ const instance = this.components[controlName]?.instance;
1014
+ if (instance && typeof instance.evaluate === 'function') {
1015
+ try {
1016
+ const result = instance.evaluate();
1017
+ if (result) {
1018
+ rates.correct++;
1019
+ }
1020
+ else {
1021
+ rates.incorrect++;
1022
+ }
1023
+ }
1024
+ catch (err) {
1025
+ console.error('Error during evaluation for component:', controlName, instance, err);
1026
+ rates.incorrect++;
1027
+ }
1028
+ }
1029
+ });
1030
+ const totalQuestions = rates.correct + rates.incorrect;
1031
+ rates.score = totalQuestions > 0 ? rates.correct / totalQuestions : 0;
1032
+ const status = rates.score >= 0.7 ? 'passed' : 'failed';
1033
+ if (status === 'passed') {
1034
+ this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
1035
+ }
1036
+ else {
1037
+ this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
1038
+ }
1039
+ return { rates, takenLesson: null };
1040
+ }
1041
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1042
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, providedIn: 'root' }); }
1043
+ }
1044
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, decorators: [{
1045
+ type: Injectable,
1046
+ args: [{
1047
+ providedIn: 'root',
1048
+ }]
1049
+ }] });
1050
+
1051
+ const EnglishEvaluationSkill = `
1052
+ You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
1053
+
1054
+ You will be provided with two pieces of information for each evaluation:
1055
+ 1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
1056
+ 2. **A conversation transcript:** This will be the dialogue between the Student and an Assistant, with each turn clearly labeled (e.g., "Student or User: ...", "Assistant: ...").
1057
+
1058
+ Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
1059
+
1060
+ Evaluate the student's performance based on the following aspects, *always keeping the student's provided level in mind* and adjusting your expectations accordingly specially with beginners give them credits just for trying:
1061
+
1062
+ * **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
1063
+ * **Vocabulary & Usage:** Evaluate the range and appropriateness of words used *relative to the expected level*. (Beginners: focus on using basic words correctly; Advanced: expect varied and idiomatic language).
1064
+ * **Cohesion & Coherence:** Evaluate how well their turns connect and make sense within the conversation *relative to the expected level*. (Beginners: focus on simple, clear responses; Advanced: expect logical flow and detailed contributions).
1065
+ * **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
1066
+
1067
+ Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
1068
+
1069
+ * **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
1070
+ * **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
1071
+ * **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
1072
+ * **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
1073
+
1074
+ Provide detailed, constructive feedback that explains the rating. Point out specific strengths and areas for improvement based on their performance *at their given level*. Use examples from the dialog if helpful. Maintain a supportive, encouraging, and educational tone appropriate for a virtual professor guiding a student at their specific stage.
1075
+ `;
1076
+
871
1077
  // Re-define or import constants if they are not exported from the component file
872
1078
  const DEFAULT_LESSON_AGENT_CARD = {
873
1079
  conversationSettings: {
@@ -949,7 +1155,13 @@ ${userInformationPrompt}`;
949
1155
  async generateConversationSettingsForLesson(lesson, settings) {
950
1156
  // TODO: Consolidate user fetching logic if possible, or ensure consistency
951
1157
  const baseLang = this.userService.getUserDataExchange()?.baseLang || 'en';
952
- const lessonText = this.lessonsService.extractTextFromHtml(lesson.textCoded);
1158
+ let lessonText = '';
1159
+ if (lesson.textCoded) {
1160
+ lessonText = this.lessonsService.extractTextFromHtml(lesson.textCoded);
1161
+ }
1162
+ else {
1163
+ lessonText = lesson.markdown;
1164
+ }
953
1165
  const userInformationPrompt = this.userService.getUserDataInformation();
954
1166
  let scenario = this._buildScenarioPrompt(lessonText, userInformationPrompt);
955
1167
  if (settings.additionalPrompt) {
@@ -981,157 +1193,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
981
1193
  }]
982
1194
  }] });
983
1195
 
984
- const EnglishEvaluationSkill = `
985
- You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
986
-
987
- You will be provided with two pieces of information for each evaluation:
988
- 1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
989
- 2. **A conversation transcript:** This will be the dialogue between the Student and an Assistant, with each turn clearly labeled (e.g., "Student or User: ...", "Assistant: ...").
990
-
991
- Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
992
-
993
- Evaluate the student's performance based on the following aspects, *always keeping the student's provided level in mind* and adjusting your expectations accordingly specially with beginners give them credits just for trying:
994
-
995
- * **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
996
- * **Vocabulary & Usage:** Evaluate the range and appropriateness of words used *relative to the expected level*. (Beginners: focus on using basic words correctly; Advanced: expect varied and idiomatic language).
997
- * **Cohesion & Coherence:** Evaluate how well their turns connect and make sense within the conversation *relative to the expected level*. (Beginners: focus on simple, clear responses; Advanced: expect logical flow and detailed contributions).
998
- * **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
999
-
1000
- Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
1001
-
1002
- * **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
1003
- * **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
1004
- * **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
1005
- * **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
1006
-
1007
- Provide detailed, constructive feedback that explains the rating. Point out specific strengths and areas for improvement based on their performance *at their given level*. Use examples from the dialog if helpful. Maintain a supportive, encouraging, and educational tone appropriate for a virtual professor guiding a student at their specific stage.
1008
- `;
1009
-
1010
- // TODO: check LessonComponentBuilders in lessons.clases.ts for origianl implementation
1011
- const DynamicComponentBuilders = {
1012
- [LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
1013
- };
1014
- const DynamicComponents = {
1015
- [LessonComponentEnum.Speaker]: SpeakerComponent,
1016
- };
1017
- class DynamicComponentsService {
1018
- constructor() {
1019
- this._dynamicComponentBuilders = { ...DynamicComponentBuilders };
1020
- this._dynamicComponents = { ...DynamicComponents };
1021
- }
1022
- registerCustomComponent(component, name) {
1023
- // this._dynamicComponentBuilders[name || component.name] = component;
1024
- this._dynamicComponents[name || component.name] = component;
1025
- }
1026
- registerCustomComponentBuilder(component, name) {
1027
- this._dynamicComponentBuilders[name || component.name] = component;
1028
- }
1029
- getDynamicComponentBuilders(type) {
1030
- return this._dynamicComponentBuilders[type];
1031
- }
1032
- getDynamicComponentClass(type) {
1033
- return this._dynamicComponents[type];
1034
- }
1035
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1036
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, providedIn: 'root' }); }
1037
- }
1038
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, decorators: [{
1039
- type: Injectable,
1040
- args: [{
1041
- providedIn: 'root',
1042
- }]
1043
- }] });
1044
-
1045
- class DCLessonRendererComponent {
1196
+ class LessonConversationService {
1046
1197
  constructor() {
1047
- // --- Signal Inputs ---
1048
- this.lessonInput = input(...(ngDevMode ? [undefined, { debugName: "lessonInput" }] : [])); // Input signal for lesson object
1049
- this.lessonIdInput = input(...(ngDevMode ? [undefined, { debugName: "lessonIdInput" }] : [])); // Input signal for lesson ID
1050
- this.settings = input(...(ngDevMode ? [undefined, { debugName: "settings" }] : []));
1051
- // --- Outputs ---
1052
- this.wordClicked = new EventEmitter(); // New output event
1053
- // --- Services ---
1054
- this.renderer = inject(Renderer2);
1055
- this.viewContainerRef = inject(ViewContainerRef);
1056
- this.toastrService = inject(TOAST_ALERTS_TOKEN);
1057
- this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
1058
- this.lessonAIService = inject(LessonAIService); // Inject the new service
1198
+ this.lessonAIService = inject(LessonAIService);
1059
1199
  this.userDataExchange = inject(USER_DATA_EXCHANGE);
1060
- this.dynamicComponentsService = inject(DynamicComponentsService);
1061
- this.uiStateService = inject(UiStateService);
1062
- // --- State Signals ---
1063
- this.lesson = signal(undefined, ...(ngDevMode ? [{ debugName: "lesson" }] : [])); // Internal lesson state signal
1064
- // chatVisible = this.uiStateService.chatDrawerVisible; // Signal for chat visibility
1065
- this.agentMasterLesson = signal(undefined, ...(ngDevMode ? [{ debugName: "agentMasterLesson" }] : [])); // Signal for agent card
1066
- this.evaluatorAgentCard = signal(undefined, ...(ngDevMode ? [{ debugName: "evaluatorAgentCard" }] : [])); // Signal for evaluator card
1067
1200
  this.conversationSettings = signal(undefined, ...(ngDevMode ? [{ debugName: "conversationSettings" }] : []));
1068
- this.evalAgentTask = signal(undefined, ...(ngDevMode ? [{ debugName: "evalAgentTask" }] : [])); // Signal for evaluator card
1069
- // backgroundTasks: Record<ConversationEvents | string, SimpleAgentTask> = {};
1070
1201
  this.conversationFlow = signal(undefined, ...(ngDevMode ? [{ debugName: "conversationFlow" }] : []));
1071
- // --- Computed Signals ---
1072
- this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url, ...(ngDevMode ? [{ debugName: "imageCover" }] : [])); // Computed signal for imageCover
1073
- // --- Properties ---
1074
- this.components = {};
1075
- this.mainForm = new FormGroup({});
1076
- this.previousTextCoded = undefined; // Store previous value
1077
- // Effect to fetch lesson data if ID is provided and lesson object isn't
1078
- effect(async () => {
1079
- const lessonInput = this.lessonInput();
1080
- const lessonId = this.lessonIdInput();
1081
- if (lessonInput) {
1082
- this.lesson.set(lessonInput); // Use input lesson directly
1083
- }
1084
- else if (lessonId && !this.lesson()) {
1085
- // Fetch only if ID exists and internal lesson is not set
1086
- console.log(`[Renderer] Effect 1: Fetching lesson ${lessonId}`);
1087
- try {
1088
- // Consider adding a loading state signal here
1089
- const fetchedLesson = await this.lessonsService.getLesson(lessonId);
1090
- this.lesson.set(fetchedLesson);
1091
- console.log('Fetched lesson:', fetchedLesson);
1092
- }
1093
- catch (error) {
1094
- console.error(`Failed to fetch lesson with ID: ${lessonId}`, error);
1095
- this.toastrService.error({ subtitle: 'Failed to load lesson data.', title: 'Error' });
1096
- this.lesson.set(undefined); // Reset lesson on error
1097
- }
1098
- finally {
1099
- // Reset loading state signal here
1100
- }
1101
- }
1102
- else if (!lessonInput && !lessonId) {
1103
- // Handle case where neither input is provided, maybe clear the lesson?
1104
- this.lesson.set(undefined);
1105
- }
1106
- }, { allowSignalWrites: true }); // Allow signal writes inside effect
1107
- // Effect to render the lesson only when textCoded value actually changes
1108
- effect(() => {
1109
- const currentLesson = this.lesson(); // Read the lesson signal
1110
- const newTextCoded = currentLesson?.textCoded; // Get the current textCoded value
1111
- this.evalAgentTask.set({
1112
- systemPrompt: EnglishEvaluationSkill,
1113
- model: { provider: 'google' },
1114
- task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
1115
- expectedResponseType: EvalResultStringDefinition,
1116
- });
1117
- // Quick fix to only render on changes textCode not all the lesson
1118
- if (newTextCoded !== this.previousTextCoded) {
1119
- if (newTextCoded !== undefined && newTextCoded !== null && currentLesson) {
1120
- this._renderLesson(currentLesson);
1121
- }
1122
- else {
1123
- this._clearLessonRendering();
1124
- }
1125
- // Update the stored previous value *after* comparison and action
1126
- this.previousTextCoded = newTextCoded;
1127
- }
1128
- else {
1129
- console.log('[Renderer] textCoded has NOT changed. Skipping render/clear.');
1130
- }
1131
- });
1202
+ this.evalAgentTask = signal(undefined, ...(ngDevMode ? [{ debugName: "evalAgentTask" }] : []));
1132
1203
  }
1133
- ngOnInit() {
1134
- // TODO: esto es mal, el goal no puede venir aqui sino pasado desde el componente que inicie la lección
1204
+ initializeConversationFlow() {
1205
+ this.evalAgentTask.set({
1206
+ systemPrompt: EnglishEvaluationSkill,
1207
+ model: { provider: 'google' },
1208
+ task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
1209
+ expectedResponseType: EvalResultStringDefinition,
1210
+ });
1135
1211
  this.conversationFlow.set({
1136
1212
  goal: {
1137
1213
  enabled: true,
@@ -1181,175 +1257,151 @@ class DCLessonRendererComponent {
1181
1257
  ],
1182
1258
  });
1183
1259
  }
1184
- // --- Rendering Logic ---
1185
- _clearLessonRendering() {
1186
- // Destroy previously created dynamic components
1187
- Object.values(this.components).forEach((compRef) => compRef.destroy());
1188
- this.components = {};
1189
- // Clear the form
1190
- this.mainForm = new FormGroup({});
1191
- // Clear the HTML content
1192
- if (this.dynamicLesson?.nativeElement) {
1193
- this.dynamicLesson.nativeElement.innerHTML = '';
1194
- }
1195
- }
1196
- _renderLesson(lessonData) {
1197
- this._clearLessonRendering(); // Clear previous state first
1198
- console.log('Rendering lesson:', lessonData.id);
1199
- // 1) Parse textCoded, create components, and build HTML structure
1200
- const { htmlContent, components } = this._parseAndCreateComponents(lessonData);
1201
- this.components = components;
1202
- // 2) Aggregate form controls from created components
1203
- this._aggregateFormControls(this.components);
1204
- // 3) Set the innerHTML of the target element
1205
- this.dynamicLesson.nativeElement.innerHTML = htmlContent;
1206
- // 4) Inject the component views into the DOM
1207
- this._injectComponentsIntoDom(this.components);
1208
- }
1209
- _parseAndCreateComponents(lessonData) {
1210
- const r1 = new RegExp('~(.+?)~', 'g');
1211
- let count = 0;
1212
- const createdComponents = {};
1213
- const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
1214
- const componentName = `dynamicComp${count}`;
1215
- count++;
1216
- const componentRef = this._createComponentReferenceWithJson(jsonCoded, lessonData);
1217
- if (!componentRef) {
1218
- console.error(`Failed to create component for: ${jsonCoded}`);
1219
- return '<!-- component creation failed -->'; // Placeholder in HTML
1260
+ async startAI(lesson, settings) {
1261
+ console.log('Requesting agent cards from LessonAIService...');
1262
+ try {
1263
+ const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(lesson, settings);
1264
+ if (conversationSettings) {
1265
+ this.conversationSettings.set(conversationSettings);
1266
+ console.log('Agent cards received and set.');
1267
+ return conversationSettings;
1220
1268
  }
1221
- createdComponents[componentName] = componentRef;
1222
- return `<span id="${componentName}"></span>`; // Return span placeholder
1223
- });
1224
- return { htmlContent, components: createdComponents };
1269
+ else {
1270
+ console.error('Failed to generate agent cards (service returned null).');
1271
+ return null;
1272
+ }
1273
+ }
1274
+ catch (error) {
1275
+ console.error('Error generating agent cards:', error);
1276
+ return null;
1277
+ }
1225
1278
  }
1226
- _aggregateFormControls(components) {
1227
- const newFormControls = {};
1228
- Object.entries(components).forEach(([name, componentRef]) => {
1229
- // Check if the instance has a control property that is a FormControl
1230
- if (componentRef.instance?.control instanceof FormControl) {
1231
- newFormControls[name] = componentRef.instance.control;
1232
- // Add required validator (consider making this configurable via component config?)
1233
- newFormControls[name].addValidators(Validators.required);
1279
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1280
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, providedIn: 'root' }); }
1281
+ }
1282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, decorators: [{
1283
+ type: Injectable,
1284
+ args: [{
1285
+ providedIn: 'root',
1286
+ }]
1287
+ }] });
1288
+
1289
+ class DCLessonRendererComponent {
1290
+ constructor() {
1291
+ // --- Signal Inputs ---
1292
+ this.lessonInput = input(...(ngDevMode ? [undefined, { debugName: "lessonInput" }] : [])); // Input signal for lesson object
1293
+ this.lessonIdInput = input(...(ngDevMode ? [undefined, { debugName: "lessonIdInput" }] : [])); // Input signal for lesson ID
1294
+ this.settings = input(...(ngDevMode ? [undefined, { debugName: "settings" }] : []));
1295
+ // --- Outputs ---
1296
+ this.wordClicked = new EventEmitter(); // New output event
1297
+ // --- View Childs ---
1298
+ this.dynamicLesson = viewChild('dynamicLesson', ...(ngDevMode ? [{ debugName: "dynamicLesson" }] : []));
1299
+ // --- Services ---
1300
+ this.renderer = inject(Renderer2);
1301
+ this.viewContainerRef = inject(ViewContainerRef);
1302
+ this.toastrService = inject(TOAST_ALERTS_TOKEN);
1303
+ this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
1304
+ this.uiStateService = inject(UiStateService);
1305
+ this.lessonRendererService = inject(LessonRendererService);
1306
+ this.lessonConversationService = inject(LessonConversationService);
1307
+ // --- State Signals ---
1308
+ this.lesson = signal(undefined, ...(ngDevMode ? [{ debugName: "lesson" }] : [])); // Internal lesson state signal
1309
+ // --- Computed Signals ---
1310
+ this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url, ...(ngDevMode ? [{ debugName: "imageCover" }] : [])); // Computed signal for imageCover
1311
+ // --- Properties ---
1312
+ this.components = {};
1313
+ this.mainForm = new FormGroup({});
1314
+ this.previousTextCoded = undefined; // Store previous value
1315
+ // Effect to fetch lesson data if ID is provided and lesson object isn't
1316
+ effect(async () => {
1317
+ const lessonInput = this.lessonInput();
1318
+ const lessonId = this.lessonIdInput();
1319
+ if (lessonInput) {
1320
+ this.lesson.set(lessonInput); // Use input lesson directly
1234
1321
  }
1235
- });
1236
- this.mainForm = new FormGroup(newFormControls); // Create the typed FormGroup
1237
- }
1238
- _injectComponentsIntoDom(components) {
1239
- Object.entries(components).forEach(([name, componentRef]) => {
1240
- const elementRef = document.getElementById(name); // Find the placeholder span
1241
- if (elementRef) {
1242
- this._addComponentToNode(componentRef, elementRef);
1322
+ else if (lessonId && !this.lesson()) {
1323
+ // Fetch only if ID exists and internal lesson is not set
1324
+ console.log(`[Renderer] Effect 1: Fetching lesson ${lessonId}`);
1325
+ try {
1326
+ // Consider adding a loading state signal here
1327
+ const fetchedLesson = await this.lessonsService.getLesson(lessonId);
1328
+ this.lesson.set(fetchedLesson);
1329
+ console.log('Fetched lesson:', fetchedLesson);
1330
+ }
1331
+ catch (error) {
1332
+ console.error(`Failed to fetch lesson with ID: ${lessonId}`, error);
1333
+ this.toastrService.error({ subtitle: 'Failed to load lesson data.', title: 'Error' });
1334
+ this.lesson.set(undefined); // Reset lesson on error
1335
+ }
1336
+ finally {
1337
+ // Reset loading state signal here
1338
+ }
1243
1339
  }
1244
- else {
1245
- console.warn(`Placeholder element with ID '${name}' not found in the DOM.`);
1340
+ else if (!lessonInput && !lessonId) {
1341
+ // Handle case where neither input is provided, maybe clear the lesson?
1342
+ this.lesson.set(undefined);
1246
1343
  }
1247
- });
1248
- }
1249
- _addComponentToNode(componentRef, nodeDOM) {
1250
- this.renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
1251
- }
1252
- _createComponentReferenceWithJson(json, currentLesson) {
1253
- try {
1254
- let lessonCodedConfig = JSON.parse(json);
1255
- //
1256
- // Attempt to find pre-configured component data in the lesson object by ID
1257
- if (lessonCodedConfig.id && currentLesson?.dynamicComponents[lessonCodedConfig.id]) {
1258
- const foundConfig = currentLesson.dynamicComponents[lessonCodedConfig.id];
1259
- if (foundConfig) {
1260
- // Merge configurations: Start with coded, override/add with found lesson config
1261
- lessonCodedConfig = { ...lessonCodedConfig, ...foundConfig };
1262
- }
1344
+ }, { allowSignalWrites: true }); // Allow signal writes inside effect
1345
+ // Effect to render the lesson only when textCoded value actually changes
1346
+ effect(() => {
1347
+ const dynamicLessonEl = this.dynamicLesson();
1348
+ const currentLesson = this.lesson(); // Read the lesson signal
1349
+ if (!dynamicLessonEl || !currentLesson) {
1350
+ return;
1263
1351
  }
1264
- const LessonClass = this.dynamicComponentsService.getDynamicComponentClass(lessonCodedConfig.component);
1265
- if (!LessonClass) {
1266
- console.error(`Component class not found for type: ${lessonCodedConfig.component}. JSON: ${json}`);
1267
- return null; // Return null if class doesn't exist
1352
+ if (currentLesson?.format === 'markdown') {
1353
+ // If markdown content exists, we clear any dynamically rendered components
1354
+ // and prevent the textCoded logic from running.
1355
+ if (this.previousTextCoded) {
1356
+ this.lessonRendererService.clearLessonRendering(dynamicLessonEl.nativeElement);
1357
+ this.mainForm = this.lessonRendererService.mainForm;
1358
+ this.previousTextCoded = undefined;
1359
+ }
1360
+ return;
1268
1361
  }
1269
- // Create component instance
1270
- const componentRef = this.viewContainerRef.createComponent(LessonClass);
1271
- if (lessonCodedConfig.inputs) {
1272
- for (const key in lessonCodedConfig.inputs) {
1273
- if (lessonCodedConfig.inputs.hasOwnProperty(key)) {
1274
- componentRef.instance[key] = lessonCodedConfig.inputs[key];
1275
- }
1362
+ const newTextCoded = currentLesson?.textCoded; // Get the current textCoded value
1363
+ // Quick fix to only render on changes textCode not all the lesson
1364
+ if (newTextCoded !== this.previousTextCoded) {
1365
+ if (newTextCoded !== undefined && newTextCoded !== null && currentLesson) {
1366
+ this.components = this.lessonRendererService.renderLesson(currentLesson, this.viewContainerRef, dynamicLessonEl.nativeElement, this.renderer);
1367
+ this.mainForm = this.lessonRendererService.mainForm;
1368
+ }
1369
+ else {
1370
+ this.lessonRendererService.clearLessonRendering(dynamicLessonEl.nativeElement);
1371
+ this.mainForm = this.lessonRendererService.mainForm;
1276
1372
  }
1373
+ // Update the stored previous value *after* comparison and action
1374
+ this.previousTextCoded = newTextCoded;
1277
1375
  }
1278
- // i think i can improve this to pass only settings not all config, Assign the configuration to the component instance
1279
- // settings data i defined in form interface, config will be all the component data including id and component type
1280
- if (lessonCodedConfig.settings) {
1281
- componentRef.instance.config = lessonCodedConfig;
1376
+ else {
1377
+ console.log('[Renderer] textCoded has NOT changed. Skipping render/clear.');
1282
1378
  }
1283
- return componentRef;
1284
- }
1285
- catch (error) {
1286
- console.error(`Error processing component JSON: ${json}`, error);
1287
- return null; // Return null on JSON parsing or other errors
1288
- }
1379
+ });
1380
+ }
1381
+ ngOnInit() {
1382
+ this.lessonConversationService.initializeConversationFlow();
1289
1383
  }
1290
- // --- Evaluation Logic ---
1291
1384
  async evaluateForms() {
1292
- this.mainForm.markAllAsTouched(); // Mark all controls for validation feedback
1293
- if (!this.mainForm.valid) {
1294
- // Trigger validation feedback on individual components visually if needed
1295
- Object.keys(this.mainForm.controls).forEach((controlName) => {
1296
- if (this.components[controlName]?.instance?.validate) {
1297
- this.components[controlName].instance.validate(); // Assuming validate method handles visual feedback
1298
- }
1299
- });
1300
- this.toastrService.warn({ subtitle: 'Por favor completa todos los ejercicios', title: 'Incompleto' });
1385
+ const result = this.lessonRendererService.evaluateForms();
1386
+ if (!result) {
1301
1387
  return;
1302
1388
  }
1303
- const rates = { correct: 0, incorrect: 0, score: 0 };
1304
- // Evaluate each component associated with a form control
1305
- Object.keys(this.mainForm.controls).forEach((controlName) => {
1306
- const instance = this.components[controlName]?.instance;
1307
- // Check if the instance has an evaluate method (duck typing is okay here)
1308
- if (instance && typeof instance.evaluate === 'function') {
1309
- try {
1310
- const result = instance.evaluate();
1311
- if (result) {
1312
- rates.correct++;
1313
- }
1314
- else {
1315
- rates.incorrect++;
1316
- }
1317
- }
1318
- catch (err) {
1319
- console.error('Error during evaluation for component:', controlName, instance, err);
1320
- rates.incorrect++; // Count errors as incorrect
1321
- }
1322
- }
1323
- });
1324
- const totalQuestions = rates.correct + rates.incorrect;
1325
- rates.score = totalQuestions > 0 ? rates.correct / totalQuestions : 0; // Avoid division by zero
1326
- const status = rates.score >= 0.7 ? 'passed' : 'failed'; // Use >= for threshold
1327
1389
  const currentLesson = this.lesson();
1328
1390
  if (!currentLesson) {
1329
1391
  console.error('Cannot save result, lesson data is missing.');
1330
1392
  this.toastrService.error({ subtitle: 'Cannot save result, lesson data missing.', title: 'Error' });
1331
1393
  return;
1332
1394
  }
1333
- const takenLesson = { id: currentLesson.id, goalCompleted: null, score: rates.score, status: status, lastAccess: new Date() };
1395
+ const status = result.rates.score >= 0.7 ? 'passed' : 'failed';
1396
+ const takenLesson = {
1397
+ id: currentLesson.id,
1398
+ goalCompleted: null,
1399
+ score: result.rates.score,
1400
+ status: status,
1401
+ lastAccess: new Date(),
1402
+ };
1334
1403
  console.log('Lesson evaluation result:', takenLesson);
1335
1404
  // TODO: Re-implement saving the taken lesson status via lessonService
1336
- // try {
1337
- // await this.lessonService.saveTakenLesson(takenLesson);
1338
- // if (status === 'passed') {
1339
- // this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Se guardó tu progreso.`, title: '¡Muy bien!' });
1340
- // } else {
1341
- // this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
1342
- // }
1343
- // } catch (error) {
1344
- // console.error('Failed to save taken lesson', error);
1345
- // this.toastrService.error({ subtitle: 'No se pudo guardar tu progreso.', title: 'Error' });
1346
- // }
1347
- if (status === 'passed') {
1348
- this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
1349
- }
1350
- else {
1351
- this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
1352
- }
1353
1405
  }
1354
1406
  // --- AI Chat Logic ---
1355
1407
  async startAI() {
@@ -1359,23 +1411,12 @@ class DCLessonRendererComponent {
1359
1411
  this.toastrService.error({ subtitle: 'Lesson data not available.', title: 'Cannot Start Chat' });
1360
1412
  return;
1361
1413
  }
1362
- console.log('Requesting agent cards from LessonAIService...');
1363
- try {
1364
- // Call the service to get the agent cards
1365
- const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(currentLesson, this.settings());
1366
- if (conversationSettings) {
1367
- this.conversationSettings.set(conversationSettings);
1368
- this.uiStateService.chatDrawerVisible.set(true);
1369
- console.log('Agent cards received and set.');
1370
- }
1371
- else {
1372
- console.error('Failed to generate agent cards (service returned null).');
1373
- this.toastrService.error({ subtitle: 'Could not prepare the AI chat session.', title: 'Error' });
1374
- }
1414
+ const conversationSettings = await this.lessonConversationService.startAI(currentLesson, this.settings());
1415
+ if (conversationSettings) {
1416
+ this.uiStateService.chatDrawerVisible.set(true);
1375
1417
  }
1376
- catch (error) {
1377
- console.error('Error generating agent cards:', error);
1378
- this.toastrService.error({ subtitle: 'An error occurred while preparing the AI chat.', title: 'Error' });
1418
+ else {
1419
+ this.toastrService.error({ subtitle: 'Could not prepare the AI chat session.', title: 'Error' });
1379
1420
  }
1380
1421
  }
1381
1422
  onVisibleChange(isVisible) {
@@ -1408,16 +1449,13 @@ class DCLessonRendererComponent {
1408
1449
  }
1409
1450
  }
1410
1451
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1411
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wordClicked: "wordClicked" }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, static: true }], ngImport: i0, template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"conversationFlow()\"\n [conversationSettings]=\"conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "conversationFlow", "agentCard", "parseDict"], outputs: ["chatEvent", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$2.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: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
1452
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wordClicked: "wordClicked" }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (lesson()?.format === 'markdown') {\n<h3>Mostrando markdown</h3>\n\n<markdown>\n {{ lesson()?.markdown }}\n</markdown>\n} @else {\n<h5>Lesson not available</h5>\n<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n}\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService?.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService?.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"lessonConversationService.conversationFlow()\"\n [conversationSettings]=\"lessonConversationService.conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "conversationFlow", "agentCard", "parseDict"], outputs: ["chatEvent", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$2.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: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
1412
1453
  }
1413
1454
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
1414
1455
  type: Component,
1415
- args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"conversationFlow()\"\n [conversationSettings]=\"conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"] }]
1456
+ args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule, MarkdownComponent], template: "@if (lesson()?.format === 'markdown') {\n<h3>Mostrando markdown</h3>\n\n<markdown>\n {{ lesson()?.markdown }}\n</markdown>\n} @else {\n<h5>Lesson not available</h5>\n<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n}\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService?.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService?.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"lessonConversationService.conversationFlow()\"\n [conversationSettings]=\"lessonConversationService.conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"] }]
1416
1457
  }], ctorParameters: () => [], propDecorators: { wordClicked: [{
1417
1458
  type: Output
1418
- }], dynamicLesson: [{
1419
- type: ViewChild,
1420
- args: ['dynamicLesson', { static: true }]
1421
1459
  }] } });
1422
1460
 
1423
1461
  class LessonNotionService {
@@ -1904,7 +1942,7 @@ class DCLessonComponentAdderComponent {
1904
1942
  }
1905
1943
  }
1906
1944
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonComponentAdderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1907
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: DCLessonComponentAdderComponent, isStandalone: true, selector: "dc-lesson-component-adder", outputs: { componentAdded: "componentAdded" }, providers: [DialogService], ngImport: i0, template: "<span>Componentes: </span>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap\">\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Selector)\"\n pTooltip=\"Agrega un selector con multiples opciones\"\n tooltipPosition=\"bottom\">\n Selector\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Speaker)\"\n pTooltip=\"Para que una palabra o frase sea reproducible\"\n tooltipPosition=\"bottom\">\n Speaker\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TextWriter)\"\n pTooltip=\"Escribe una respuesta en un cuadro de texto\"\n tooltipPosition=\"bottom\">\n Text\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de un verbo\"\n tooltipPosition=\"bottom\">\n Verb\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.WordSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de una palabra\"\n tooltipPosition=\"bottom\">\n Palabra\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TranslationSwitcher)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Traducci\u00F3n\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.PlayWord)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Play Word\n </p-button>\n <!-- Add other buttons here if needed, following the same pattern -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] }); }
1945
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: DCLessonComponentAdderComponent, isStandalone: true, selector: "dc-lesson-component-adder", outputs: { componentAdded: "componentAdded" }, providers: [DialogService], ngImport: i0, template: "<span>Componentes: </span>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap\">\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Selector)\"\n pTooltip=\"Agrega un selector con multiples opciones\"\n tooltipPosition=\"bottom\">\n Selector\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Speaker)\"\n pTooltip=\"Para que una palabra o frase sea reproducible\"\n tooltipPosition=\"bottom\">\n Speaker\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TextWriter)\"\n pTooltip=\"Escribe una respuesta en un cuadro de texto\"\n tooltipPosition=\"bottom\">\n Text\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de un verbo\"\n tooltipPosition=\"bottom\">\n Verb\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.WordSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de una palabra\"\n tooltipPosition=\"bottom\">\n Palabra\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TranslationSwitcher)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Traducci\u00F3n\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.PlayWord)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Play Word\n </p-button>\n <!-- Add other buttons here if needed, following the same pattern -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] }); }
1908
1946
  }
1909
1947
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonComponentAdderComponent, decorators: [{
1910
1948
  type: Component,
@@ -1915,99 +1953,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
1915
1953
 
1916
1954
  class DCLessonMetadataEditorComponent {
1917
1955
  constructor() {
1918
- // Use signal for input lesson data
1919
- this.lesson = signal(undefined, ...(ngDevMode ? [{ debugName: "lesson" }] : [])); // The lesson data itself
1920
- this.isLoadingLesson = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingLesson" }] : [])); // Shared loading state
1921
1956
  // Outputs for actions
1922
1957
  this.saveRequest = new EventEmitter();
1923
1958
  this.importNotionRequest = new EventEmitter();
1924
1959
  this.improveNotionRequest = new EventEmitter();
1925
- // Removed generateAIRequest Output as it's handled internally now
1926
- // Output removed as the component now updates the input signal directly.
1927
- // @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
1928
1960
  // Injected Services
1929
1961
  this.#lessonUtilsService = inject(LessonUtilsService);
1930
1962
  this.#toastService = inject(TOAST_ALERTS_TOKEN);
1931
1963
  this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
1932
- this.#turndownService = new TurndownService(); // Instantiate TurndownService
1964
+ this.turndownService = new TurndownService(); // Instantiate TurndownService
1933
1965
  }
1934
- // Removed generateAIRequest Output as it's handled internally now
1935
- // Output removed as the component now updates the input signal directly.
1936
- // @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
1937
1966
  // Injected Services
1938
1967
  #lessonUtilsService;
1939
1968
  #toastService;
1940
- #turndownService; // Instantiate TurndownService
1941
- // Method to handle property changes for ROOT properties (e.g., level)
1942
- onRootPropertyChange(property, value) {
1943
- this.lesson.update((current) => {
1944
- if (!current)
1945
- return undefined;
1946
- // Avoid updating metadata directly here
1947
- // if (property === 'metadata') {
1948
- // console.warn('Direct metadata updates should use onMetadataPropertyChange');
1949
- // return current;
1950
- // }
1951
- return { ...current, [property]: value };
1952
- });
1953
- // Emit removed - signal is updated directly above.
1954
- // this.propertyChange.emit({ propertyPath: property, value });
1955
- }
1956
- // Method to handle property changes for APP EXTENSION properties
1957
- onAppExtensionPropChange(property, value) {
1958
- this.lesson.update((current) => {
1959
- if (!current)
1960
- return undefined;
1961
- // Ensure appExtension exists, initialize if not
1962
- // Convert value to number specifically for 'level'
1963
- const finalValue = property === 'level' ? Number(value) : value;
1964
- const updatedAppExtension = { ...(current.extensions ?? {}), [property]: finalValue };
1965
- return { ...current, extensions: updatedAppExtension };
1966
- });
1969
+ ngOnInit() {
1970
+ // console.log(this.lesson(), this.form);
1967
1971
  }
1968
1972
  onManageablePropertyChange(property, value) {
1969
- this.lesson.update((current) => {
1970
- if (!current)
1971
- return undefined;
1972
- const updatedManageable = { ...(current.manageable ?? {}), [property]: value };
1973
- return { ...current, manageable: updatedManageable };
1974
- });
1973
+ // this.lesson.update((current) => {
1974
+ // if (!current) return undefined;
1975
+ // const updatedManageable = { ...(current.manageable ?? {}), [property]: value };
1976
+ // return { ...current, manageable: updatedManageable as IManageable };
1977
+ // });
1975
1978
  }
1976
1979
  onAuditablePropertyChange(property, value) {
1977
- this.lesson.update((current) => {
1978
- if (!current)
1979
- return undefined;
1980
- const updatedAuditable = { ...(current.auditable ?? {}), [property]: value };
1981
- return { ...current, auditable: updatedAuditable };
1982
- });
1983
- }
1984
- // Methods to emit action requests
1985
- emitSaveRequest() {
1986
- this.saveRequest.emit();
1980
+ // this.lesson.update((current) => {
1981
+ // if (!current) return undefined;
1982
+ // const updatedAuditable = { ...(current.auditable ?? {}), [property]: value };
1983
+ // return { ...current, auditable: updatedAuditable as IAuditable };
1984
+ // });
1987
1985
  }
1988
- emitImportNotionRequest() {
1989
- this.importNotionRequest.emit();
1986
+ // New methods to handle events with proper casting
1987
+ handlePromptInputChange(event) {
1988
+ const target = event.target;
1989
+ this.onAuditablePropertyChange('prompt', target.value);
1990
1990
  }
1991
- emitImproveNotionRequest() {
1992
- this.improveNotionRequest.emit();
1991
+ handleStatusChange(event) {
1992
+ const target = event.target;
1993
+ this.onManageablePropertyChange('status', target.checked ? 'published' : 'draft');
1993
1994
  }
1994
1995
  /**
1995
1996
  * Generates lesson content using AI, saving the current state first.
1996
1997
  * Moved from DCLessonEditorComponent.
1997
1998
  */
1998
1999
  async generateByAI() {
1999
- const currentLesson = this.lesson(); // Get current value
2000
+ const currentLesson = this.lesson; // Get current value
2000
2001
  if (!currentLesson?.id) {
2001
2002
  this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
2002
2003
  return;
2003
2004
  }
2004
- this.isLoadingLesson.set(true);
2005
+ this.isLoadingLesson = true;
2005
2006
  try {
2006
2007
  const rawHtmlContent = currentLesson.textCoded || '';
2007
2008
  if (!rawHtmlContent) {
2008
2009
  console.warn('No HTML content found in lesson to process. taking just description');
2009
2010
  this.#toastService.info({ title: 'Contenido lección desde 0', subtitle: 'Solo se usará el prompt' });
2010
- const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), 'Create content from description');
2011
+ const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson, 'Create content from description');
2011
2012
  // Convert and save the generated content
2012
2013
  await this._convertMarkdownToHtmlAndSave(improvedMarkdown); // Use extracted method
2013
2014
  }
@@ -2019,13 +2020,13 @@ class DCLessonMetadataEditorComponent {
2019
2020
  this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudo guardar antes de generar con IA.' });
2020
2021
  throw new Error('Failed to save before AI generation');
2021
2022
  }
2022
- this.lesson.set(savedLesson);
2023
+ this.lesson = savedLesson;
2023
2024
  // Replace encoded JSON with actual text before Markdown conversion
2024
2025
  const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
2025
2026
  // Convert the processed HTML (with text instead of JSON) to Markdown
2026
- const markdownText = this.#turndownService.turndown(processedHtmlContent);
2027
+ const markdownText = this.turndownService.turndown(processedHtmlContent);
2027
2028
  // Use the updated lesson signal value for AI improvement
2028
- const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), markdownText);
2029
+ const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson, markdownText);
2029
2030
  // Convert and save the improved content
2030
2031
  await this._convertMarkdownToHtmlAndSave(improvedMarkdown); // Use extracted method
2031
2032
  }
@@ -2036,7 +2037,7 @@ class DCLessonMetadataEditorComponent {
2036
2037
  // this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
2037
2038
  }
2038
2039
  finally {
2039
- this.isLoadingLesson.set(false); // Stop loading
2040
+ this.isLoadingLesson = false; // Stop loading
2040
2041
  }
2041
2042
  }
2042
2043
  /**
@@ -2072,10 +2073,10 @@ class DCLessonMetadataEditorComponent {
2072
2073
  // Convert the improved Markdown back to HTML before setting it
2073
2074
  const improvedHtml = marked(improvedMarkdown);
2074
2075
  // Update the signal directly
2075
- this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
2076
+ // this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
2076
2077
  // Save the AI-generated content
2077
2078
  // Ensure lesson() is not undefined before saving
2078
- const lessonToSave = this.lesson();
2079
+ const lessonToSave = this.lesson;
2079
2080
  if (lessonToSave) {
2080
2081
  await this.lessonsService.postLesson(lessonToSave);
2081
2082
  this.#toastService.success({ title: 'Contenido generado', subtitle: 'Se generó y guardó el contenido con IA.' });
@@ -2100,21 +2101,21 @@ class DCLessonMetadataEditorComponent {
2100
2101
  * and updates the lesson signal if successful.
2101
2102
  */
2102
2103
  async triggerGenerateDescriptionAI() {
2103
- const currentLesson = this.lesson();
2104
+ const currentLesson = this.lesson;
2104
2105
  if (!currentLesson) {
2105
2106
  this.#toastService.warn({ title: 'Lección no cargada', subtitle: 'Espera a que la lección se cargue.' });
2106
2107
  return;
2107
2108
  }
2108
2109
  const generatedDescription = await this.#lessonUtilsService.generateDescriptionWithAI(currentLesson);
2109
2110
  if (generatedDescription) {
2110
- this.lessonForm.controls['description'].setValue(generatedDescription);
2111
+ // this.form.controls['description'].setValue(generatedDescription);
2111
2112
  }
2112
2113
  }
2113
2114
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2114
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { lesson: "lesson", lessonForm: "lessonForm", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest" }, ngImport: i0, template: "@if (lesson(); as currentLesson) {\n<div [formGroup]=\"lessonForm\">\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div>\n\n <!-- Use one-way binding and ngModelChange for signals -->\n <div>\n <input pInputText style=\"width: 100%\" formControlName=\"title\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" formControlName=\"description\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson()\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [ngModel]=\"currentLesson.auditable?.prompt\"\n (ngModelChange)=\"onAuditablePropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson()\" (click)=\"generateByAI()\" />\n </div>\n\n <div style=\"margin-top: 10px\">\n <label class=\"checkbox-container\" style=\"margin-right: 15px\">\n <input\n type=\"checkbox\"\n [ngModel]=\"currentLesson.manageable?.status === 'published'\"\n (ngModelChange)=\"onManageablePropertyChange('status', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input\n pInputText\n [ngModel]=\"currentLesson.extensions?.['level']\"\n (ngModelChange)=\"onAppExtensionPropChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (currentLesson.extensions) {\n <div>\n {{ currentLesson.extensions?.['baseLang'] | flagEmoji }} -> {{ currentLesson.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ currentLesson.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n </div>\n</div>\n} @else {\n<!-- Optional: Show a loading state or placeholder if lesson is undefined -->\n<p>Cargando datos de la lecci\u00F3n...</p>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: // Added Pipe
2115
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { form: "form", lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest" }, ngImport: i0, template: "<div>\n <!-- <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div> -->\n\n <div>\n <div>\n <span>Nombre de La lecci\u00F3n</span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['name']\" type=\"text\" placeholder=\"Agrega un nombre\" />\n </div>\n <div>\n <span>T\u00EDtulo </span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['title']\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n\n <div style=\"margin-top: 4px\">\n <span>Descripci\u00F3n </span>\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['description']\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [value]=\"lesson?.auditable?.prompt || ''\"\n (input)=\"handlePromptInputChange($event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\" />\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input pInputText [value]=\"lesson?.extensions?.['level'] || ''\" type=\"number\" placeholder=\"Nivel\" style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (lesson?.extensions) {\n <div>\n {{ lesson?.extensions?.['baseLang'] | flagEmoji }} -> {{ lesson?.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ lesson?.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ lesson?.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: // Added Pipe
2115
2116
  InputGroupModule }, { kind: "component", type: i5.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i6.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "pipe", type: // Added TooltipModule
2116
2117
  FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
2117
- LangDescTranslationPipe, name: "langDesc" }] }); }
2118
+ LangDescTranslation, name: "langDesc" }] }); }
2118
2119
  }
2119
2120
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonMetadataEditorComponent, decorators: [{
2120
2121
  type: Component,
@@ -2126,14 +2127,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
2126
2127
  ReactiveFormsModule,
2127
2128
  TooltipModule, // Added TooltipModule
2128
2129
  FlagLanguagePipe, // Added Pipe
2129
- LangDescTranslationPipe, // Added Pipe
2130
+ LangDescTranslation, // Added Pipe
2130
2131
  InputGroupModule,
2131
2132
  DividerModule,
2132
- ], template: "@if (lesson(); as currentLesson) {\n<div [formGroup]=\"lessonForm\">\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div>\n\n <!-- Use one-way binding and ngModelChange for signals -->\n <div>\n <input pInputText style=\"width: 100%\" formControlName=\"title\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" formControlName=\"description\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson()\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [ngModel]=\"currentLesson.auditable?.prompt\"\n (ngModelChange)=\"onAuditablePropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson()\" (click)=\"generateByAI()\" />\n </div>\n\n <div style=\"margin-top: 10px\">\n <label class=\"checkbox-container\" style=\"margin-right: 15px\">\n <input\n type=\"checkbox\"\n [ngModel]=\"currentLesson.manageable?.status === 'published'\"\n (ngModelChange)=\"onManageablePropertyChange('status', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input\n pInputText\n [ngModel]=\"currentLesson.extensions?.['level']\"\n (ngModelChange)=\"onAppExtensionPropChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (currentLesson.extensions) {\n <div>\n {{ currentLesson.extensions?.['baseLang'] | flagEmoji }} -> {{ currentLesson.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ currentLesson.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n </div>\n</div>\n} @else {\n<!-- Optional: Show a loading state or placeholder if lesson is undefined -->\n<p>Cargando datos de la lecci\u00F3n...</p>\n}\n" }]
2133
- }], propDecorators: { lesson: [{
2133
+ ], template: "<div>\n <!-- <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div> -->\n\n <div>\n <div>\n <span>Nombre de La lecci\u00F3n</span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['name']\" type=\"text\" placeholder=\"Agrega un nombre\" />\n </div>\n <div>\n <span>T\u00EDtulo </span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['title']\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n\n <div style=\"margin-top: 4px\">\n <span>Descripci\u00F3n </span>\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['description']\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [value]=\"lesson?.auditable?.prompt || ''\"\n (input)=\"handlePromptInputChange($event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\" />\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input pInputText [value]=\"lesson?.extensions?.['level'] || ''\" type=\"number\" placeholder=\"Nivel\" style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (lesson?.extensions) {\n <div>\n {{ lesson?.extensions?.['baseLang'] | flagEmoji }} -> {{ lesson?.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ lesson?.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ lesson?.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n</div>\n" }]
2134
+ }], propDecorators: { form: [{
2134
2135
  type: Input,
2135
2136
  args: [{ required: true }]
2136
- }], lessonForm: [{
2137
+ }], lesson: [{
2137
2138
  type: Input,
2138
2139
  args: [{ required: true }]
2139
2140
  }], isLoadingLesson: [{
@@ -2147,53 +2148,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
2147
2148
  type: Output
2148
2149
  }] } });
2149
2150
 
2150
- class DCLessonEditorComponent {
2151
- // Services
2152
- #activatedRoute; // Re-inject as it's needed for navigation
2153
- #lessonNotionService;
2154
- #lessonUtilsService;
2155
- #router;
2156
- #toastService;
2157
- #loadingBarService;
2158
- #formBuilder;
2151
+ class LessonFormEditorService {
2152
+ constructor() {
2153
+ this.fb = inject(FormBuilder);
2154
+ this.formUtils = inject(FormUtilsService);
2155
+ this.formatOptions = [
2156
+ { label: 'HTML', value: 'html' },
2157
+ { label: 'Markdown', value: 'markdown' },
2158
+ ];
2159
+ }
2160
+ createLessonForm() {
2161
+ return this.fb.group({
2162
+ version: ['1.0'],
2163
+ id: [''],
2164
+ name: [''],
2165
+ title: [''],
2166
+ description: [''],
2167
+ format: ['html'],
2168
+ lang: [''],
2169
+ characterCard: [],
2170
+ conversationSettings: [],
2171
+ metaApp: [],
2172
+ conversationFlow: [],
2173
+ textCoded: [''],
2174
+ manageable: this.formUtils.createManageableFormGroup(),
2175
+ learnable: this.formUtils.createLearnableFormGroup(),
2176
+ });
2177
+ }
2178
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2179
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, providedIn: 'root' }); }
2180
+ }
2181
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, decorators: [{
2182
+ type: Injectable,
2183
+ args: [{
2184
+ providedIn: 'root',
2185
+ }]
2186
+ }] });
2187
+
2188
+ class DCLessonEditorComponent extends EntityBaseFormComponent {
2159
2189
  constructor() {
2190
+ super(...arguments);
2191
+ this.lessonFormEditorService = inject(LessonFormEditorService);
2192
+ this.markdownService = inject(MarkdownService);
2193
+ this.form = this.lessonFormEditorService.createLessonForm();
2194
+ this.formatOptions = this.lessonFormEditorService.formatOptions;
2195
+ this.entityCommunicationService = inject(LESSONS_TOKEN);
2196
+ this.htmlTemporal = '';
2160
2197
  // Services
2161
- this.#activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
2162
- this.#lessonNotionService = inject(LessonNotionService);
2163
- this.#lessonUtilsService = inject(LessonUtilsService);
2164
- this.#router = inject(Router);
2198
+ this.activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
2199
+ this.lessonNotionService = inject(LessonNotionService);
2200
+ this.lessonUtilsService = inject(LessonUtilsService);
2165
2201
  this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
2166
- this.#toastService = inject(TOAST_ALERTS_TOKEN);
2167
- this.#loadingBarService = inject(LoadingBarService);
2202
+ this.loadingBarService = inject(LoadingBarService);
2168
2203
  this.defaultLessonsService = inject(DefaultLessonsService);
2169
2204
  this.promptService = inject(PromptService);
2170
2205
  this.ngxVertexService = inject(NgxVertexService);
2171
- this.cdr = inject(ChangeDetectorRef);
2172
2206
  this.dynamicComponentsBuilderService = inject(DynamicComponentsBuilderService);
2173
- this.#formBuilder = inject(FormBuilder);
2174
- // Signals States
2175
- this.lessonId = toSignal(inject(ActivatedRoute).paramMap.pipe(map((params) => params.get('id'))));
2176
- this.lesson = signal(undefined, ...(ngDevMode ? [{ debugName: "lesson" }] : []));
2177
2207
  this.isLoadingLesson = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingLesson" }] : []));
2178
- // Computed Signals
2179
- this.coverImageUrl = computed(() => {
2180
- // Priority Order 1 Metadata Banner, 2 Banner, 3 Media First Images, 4 Default Banner TODO: reveme banner after migration to Content
2181
- const currentLesson = this.lesson();
2182
- if (currentLesson?.assets?.banner?.url) {
2183
- return currentLesson.assets.banner.url;
2184
- }
2185
- else if (currentLesson?.media?.images?.find((img) => img.type === 'cover')) {
2186
- // 3 Media First Images
2187
- return currentLesson.media.images.find((img) => img.type === 'cover')?.url;
2188
- }
2189
- return '/assets/defaults/images/default_banner.webp';
2190
- }, ...(ngDevMode ? [{ debugName: "coverImageUrl" }] : []));
2191
2208
  // Computed signal to get dynamic components as an array for easier iteration in the template
2192
2209
  this.dynamicComponentsArray = computed(() => {
2193
- const currentLesson = this.lesson();
2194
- if (currentLesson?.dynamicComponents) {
2195
- return Object.values(currentLesson.dynamicComponents);
2196
- }
2210
+ // const currentLesson = this.lesson();
2211
+ // if (currentLesson?.dynamicComponents) {
2212
+ // return Object.values(currentLesson.dynamicComponents);
2213
+ // }
2197
2214
  return []; // Return empty array if no lesson or no dynamic components
2198
2215
  }, ...(ngDevMode ? [{ debugName: "dynamicComponentsArray" }] : []));
2199
2216
  // States
@@ -2201,57 +2218,21 @@ class DCLessonEditorComponent {
2201
2218
  this.editor = BalloonEditor;
2202
2219
  this.lessonComponentEnum = LessonComponentEnum;
2203
2220
  this.coverStorageSettings = {
2204
- path: `lessons/${this.lessonId()}/covers`,
2221
+ path: `lessons/${this.entityId()}/covers`,
2205
2222
  fileName: 'cover',
2206
2223
  cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
2207
2224
  };
2208
- this.promptsVisible = false;
2209
- this.lessonForm = this.#formBuilder.group({
2210
- title: [''],
2211
- description: [''],
2212
- });
2213
- // Effect to fetch lesson data when ID changes
2214
- effect(async () => {
2215
- const id = this.lessonId();
2216
- console.log('Lesson ID Signal:', id);
2217
- if (id) {
2218
- this.isLoadingLesson.set(true); // Start loading
2219
- try {
2220
- const fetchedLesson = await this.lessonsService.getLesson(id);
2221
- if (fetchedLesson) {
2222
- this.lesson.set(fetchedLesson);
2223
- this.lessonForm.patchValue(fetchedLesson, { emitEvent: false });
2224
- }
2225
- else {
2226
- this.lesson.set(undefined); // Reset if not found
2227
- this.#toastService.warn({ title: 'No se encontró la lección', subtitle: 'Quizá el id es incorrecto' });
2228
- // Optional: Navigate away or show a specific "not found" state
2229
- // this.#router.navigate(['/path/to/lessons']);
2230
- }
2231
- }
2232
- catch (error) {
2233
- console.error('Error fetching lesson:', error);
2234
- this.lesson.set(undefined); // Reset on error
2235
- this.#toastService.error({ title: 'Error al cargar la lección', subtitle: 'Intenta de nuevo más tarde' });
2236
- }
2237
- finally {
2238
- this.isLoadingLesson.set(false); // Stop loading
2239
- }
2240
- }
2241
- else {
2242
- // Handle case for new lesson (ID is null/undefined)
2243
- this.lesson.set({ textCoded: `<h1>Nueva lección </h1> <p> Texto aquí</p>`, tags: [] }); // Set default new lesson structure
2244
- this.saveLesson();
2245
- this.isLoadingLesson.set(false); // Ensure loading is off
2246
- }
2247
- });
2248
- this.lessonForm.valueChanges.subscribe((value) => {
2249
- this.lesson.update((current) => {
2250
- if (!current)
2251
- return undefined;
2252
- return { ...current, ...value };
2253
- });
2254
- });
2225
+ }
2226
+ patchForm(entity) {
2227
+ console.log(this.form);
2228
+ if (this.entity().format === 'markdown') {
2229
+ this.htmlTemporal = this.markdownService.parse(entity.markdown);
2230
+ }
2231
+ else {
2232
+ this.htmlTemporal = entity.textCoded;
2233
+ }
2234
+ // console.log(this.htmlTemporal);
2235
+ this.form.patchValue(entity);
2255
2236
  }
2256
2237
  /**
2257
2238
  * Updates a specific property on the lesson signal.
@@ -2261,84 +2242,71 @@ class DCLessonEditorComponent {
2261
2242
  */
2262
2243
  updateLessonProperty(property, value) {
2263
2244
  console.log('Updating property:', property, value);
2264
- this.lesson.update((currentLesson) => {
2245
+ this.entity.update((currentLesson) => {
2265
2246
  if (!currentLesson)
2266
2247
  return undefined;
2267
2248
  return { ...currentLesson, [property]: value };
2268
2249
  });
2269
2250
  }
2270
- onAssetsChange(updatedAssets) {
2271
- this.lesson.update((currentLesson) => {
2251
+ updateHtmlTextCoded(_, value) {
2252
+ this.entity.update((currentLesson) => {
2272
2253
  if (!currentLesson)
2273
2254
  return undefined;
2274
- return { ...currentLesson, assets: updatedAssets };
2255
+ return { ...currentLesson, textCoded: value };
2275
2256
  });
2257
+ this.form.controls.textCoded.setValue(value);
2276
2258
  }
2277
- onTagRemove(tag) {
2278
- this.lesson.update((currentLesson) => {
2259
+ onAssetsChange(updatedAssets) {
2260
+ console.log(updatedAssets);
2261
+ this.entity.update((currentLesson) => {
2279
2262
  if (!currentLesson)
2280
2263
  return undefined;
2281
- const updatedTags = currentLesson.tags.filter((text) => text !== tag.text);
2282
- return { ...currentLesson, tags: updatedTags };
2264
+ return { ...currentLesson, assets: updatedAssets };
2283
2265
  });
2284
2266
  }
2285
- onTagAdd(tag) {
2286
- if (tag.value) {
2287
- this.lesson.update((currentLesson) => {
2288
- if (!currentLesson)
2289
- return undefined;
2290
- // Avoid duplicate tags if necessary
2291
- if (currentLesson.tags.includes(tag.value)) {
2292
- return currentLesson;
2293
- }
2294
- const updatedTags = [...currentLesson.tags, tag.value];
2295
- return { ...currentLesson, tags: updatedTags };
2296
- });
2297
- }
2298
- tag.input.nativeElement.value = ''; // Clear input
2299
- }
2300
2267
  async saveLesson(event) {
2268
+ // TODO: Revisar por ahora uso el método del padre para guardar. pero creo que este era para guardar la lección.
2301
2269
  event?.preventDefault();
2302
- const currentLesson = this.lesson();
2270
+ const currentLesson = this.entity();
2303
2271
  if (!currentLesson) {
2304
- this.#toastService.error({ title: 'Error', subtitle: 'No hay datos de lección para guardar' });
2272
+ this.toastService.error({ title: 'Error', subtitle: 'No hay datos de lección para guardar' });
2305
2273
  return undefined;
2306
2274
  }
2307
2275
  // Clean orphaned components before saving
2308
- const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLesson);
2276
+ const lessonToSave = this.lessonUtilsService.cleanOrphanedComponents(currentLesson);
2309
2277
  // TODO: Implement optimization for saving only changed data.
2310
2278
  // This requires comparing lessonToSave with the initially fetched state.
2311
2279
  this.isLoadingLesson.set(true); // Indicate saving
2312
2280
  try {
2313
2281
  // Use the cleaned lesson object for saving
2314
2282
  const savedLesson = await this.lessonsService.postLesson(lessonToSave);
2315
- const currentId = this.lessonId();
2283
+ const currentId = this.entityId();
2316
2284
  if (!currentId) {
2317
2285
  // No se como guardar los extras aunt
2318
2286
  // It was a new lesson, now it has an ID. Navigate.
2319
- this.#toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
2287
+ this.toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
2320
2288
  // The effect should automatically fetch the lesson again after navigation due to paramMap change.
2321
- this.#router.navigate(['./', savedLesson.id], { relativeTo: this.#activatedRoute });
2289
+ this.router.navigate(['./', savedLesson.id], { relativeTo: this.activatedRoute });
2322
2290
  }
2323
2291
  else {
2324
2292
  // It was an existing lesson, update the signal with the potentially updated data from the backend.
2325
- this.lesson.set(savedLesson);
2326
- this.#toastService.info({ title: 'Se guardaron los cambios en la lección', subtitle: 'Guardado' });
2293
+ this.entity.set(savedLesson);
2294
+ this.toastService.info({ title: 'Se guardaron los cambios en la lección', subtitle: 'Guardado' });
2327
2295
  // Call the service method for validation
2328
- this.#lessonUtilsService.validateAudios(this.lesson());
2296
+ this.lessonUtilsService.validateAudios(this.entity());
2329
2297
  }
2330
2298
  return savedLesson;
2331
2299
  }
2332
2300
  catch (error) {
2333
2301
  // Type error
2334
2302
  console.error('Error saving lesson:', error);
2335
- this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudieron guardar los cambios' });
2303
+ this.toastService.error({ title: 'Error al guardar', subtitle: 'No se pudieron guardar los cambios' });
2336
2304
  return undefined;
2337
2305
  }
2338
2306
  finally {
2339
2307
  this.isLoadingLesson.set(false); // Finish saving indication
2340
2308
  }
2341
- } // Add missing closing brace for saveLesson
2309
+ }
2342
2310
  // Removed openComponentBuilder method
2343
2311
  /**
2344
2312
  * Handles the event emitted when a component is added via the adder component.
@@ -2350,7 +2318,7 @@ class DCLessonEditorComponent {
2350
2318
  if (newComponent?.id) {
2351
2319
  console.log('Component builder closed, result received in editor:', newComponent);
2352
2320
  // Update the lesson signal, adding the transformed component to the dynamicComponents object
2353
- this.lesson.update((currentLesson) => {
2321
+ this.entity.update((currentLesson) => {
2354
2322
  if (!currentLesson)
2355
2323
  return undefined;
2356
2324
  // Ensure dynamicComponents object exists, initialize if not
@@ -2363,54 +2331,17 @@ class DCLessonEditorComponent {
2363
2331
  // Return the updated lesson state
2364
2332
  return { ...currentLesson, dynamicComponents: updatedDynamicComponents };
2365
2333
  });
2366
- // Optionally save the lesson after adding the component
2367
- // this.saveLesson();
2368
- }
2369
- }
2370
- // isLoadingLesson signal is used directly
2371
- // Removed generateByAI and _extractTextFromEncodedJson methods.
2372
- // This logic is now handled within DCLessonMetadataEditorComponent.
2373
- /**
2374
- * Handles the image upload event, updates the lesson signal via the service, and saves.
2375
- * @param event The image upload event data.
2376
- */
2377
- async onImageUploaded(event) {
2378
- this.#lessonUtilsService.uploadCover(this.lesson, event);
2379
- await this.saveLesson();
2380
- }
2381
- /**
2382
- * Imports lesson content from Notion using the LessonNotionService.
2383
- */
2384
- async importFromNotion() {
2385
- // Use the service's loading state or manage locally
2386
- this.isLoadingLesson.set(true);
2387
- try {
2388
- const newContent = await this.#lessonNotionService.importAndLinkLessonFromNotion(this.lesson(), this.lessonId());
2389
- if (newContent !== null) {
2390
- // Update the lesson signal's textCoded property
2391
- this.updateLessonProperty('textCoded', newContent);
2392
- // Toast success is handled within the service now
2393
- }
2394
- // If newContent is null, the service handled errors/toasts
2395
- }
2396
- finally {
2397
- // Ensure loading state is reset regardless of service outcome
2398
- // If observing service state: this.isLoadingLesson.set(this.#lessonNotionService.isLoading());
2399
- this.isLoadingLesson.set(false); // Keep local loading for now
2400
2334
  }
2401
2335
  }
2402
- /**
2403
- * Calls the LessonNotionService to improve the lesson using AI based on Notion content.
2404
- */
2405
2336
  async improveNotionWithAI() {
2406
- await this.#lessonNotionService.improveLessonWithNotionAI(this.lesson());
2337
+ await this.lessonNotionService.improveLessonWithNotionAI(this.entity());
2407
2338
  }
2408
2339
  showComponentDetails(data) {
2409
2340
  alert('showComponentDetails' + JSON.stringify(data));
2410
2341
  }
2411
2342
  async generateBanner() {
2412
- this.#toastService.info({ title: 'Generando prompt de sugerencia', subtitle: 'Por favor, espera' });
2413
- const prompt = this.lessonsService.getPrompts().banner(this.lesson());
2343
+ this.toastService.info({ title: 'Generando prompt de sugerencia', subtitle: 'Por favor, espera' });
2344
+ const prompt = this.lessonsService.getPrompts().banner(this.entity());
2414
2345
  const geminiRes = await this.ngxVertexService.generateText([{ role: ChatRoleVertex.User, content: prompt }]);
2415
2346
  this.promptService
2416
2347
  .openPrompt({
@@ -2422,32 +2353,16 @@ class DCLessonEditorComponent {
2422
2353
  })
2423
2354
  .then((promptResult) => {
2424
2355
  if (promptResult) {
2425
- this.#loadingBarService.showIndeterminate();
2426
- this.defaultLessonsService.generateBanner(promptResult, this.lessonId()).then((result) => {
2356
+ this.loadingBarService.showIndeterminate();
2357
+ this.defaultLessonsService.generateBanner(promptResult, this.entityId()).then((result) => {
2427
2358
  if (result) {
2428
2359
  alert('Revisar como actualizar el banner');
2429
2360
  // this.updateLessonProperty('banner', (result as any).banner);
2430
2361
  }
2431
- this.#loadingBarService.successAndHide();
2362
+ this.loadingBarService.successAndHide();
2432
2363
  });
2433
2364
  }
2434
2365
  });
2435
- // const imagePrompt = prompt('alguna idea de lo que quieres ver?');
2436
- // this.#loadingBarService.showIndeterminate();
2437
- // const result = await this.defaultLessonsService.generateBanner(imagePrompt, this.lessonId());
2438
- // if (result) {
2439
- // this.updateLessonProperty('banner', result.banner);
2440
- // }
2441
- // console.log('Generated banner:', result);
2442
- //
2443
- // this.#loadingBarService.successAndHide();
2444
- }
2445
- showPrompts() {
2446
- this.promptsVisible = true;
2447
- const promptsFn = this.lessonsService.getPrompts();
2448
- this.prompts = { banner: promptsFn.banner(this.lesson()), content: promptsFn.content(this.lesson()), description: promptsFn.description(this.lesson()) };
2449
- console.log(this.prompts);
2450
- this.cdr.markForCheck();
2451
2366
  }
2452
2367
  editComponent(comp) {
2453
2368
  console.log('Edit component:', comp);
@@ -2455,39 +2370,51 @@ class DCLessonEditorComponent {
2455
2370
  this.onComponentAdded(result);
2456
2371
  });
2457
2372
  }
2458
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2459
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], viewQueries: [{ propertyName: "target", first: true, predicate: ["target"], descendants: true, read: ViewContainerRef }, { propertyName: "dhtml", first: true, predicate: ["dhtml"], descendants: true, static: true }], ngImport: i0, template: "<!-- <div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n\n <p-button\n (click)=\"generateBanner()\"\n class=\"generate-banner-btn\"\n icon=\"pi pi-sparkles\"\n severity=\"primary\"\n [rounded]=\"true\"\n [raised]=\"true\"\n pTooltip=\"Generar Banner AI\"\n tooltipPosition=\"left\"></p-button>\n\n <p-button class=\"prompt-visual\" icon=\"pi pi-info\" label=\"Ver Prompts\" [link]=\"true\" (click)=\"showPrompts()\" />\n</div> -->\n\n<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader [assets]=\"lesson()?.assets\" storagePath=\"lessons/{{ lesson()?.id }}\" (assetsChange)=\"onAssetsChange($event)\"></assets-loader>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"lesson()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"lesson()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-viewer [data]=\"lesson()?.manageable\"></dc-manageable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"lesson()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"lesson()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<!-- <dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [lessonForm]=\"lessonForm\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\">\n</dc-lesson-metadata-editor> -->\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"lesson()?.textCoded\"\n (ngModelChange)=\"updateLessonProperty('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"lesson()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <!-- Removed p-speeddial -->\n <p-button icon=\"pi pi-save\" (click)=\"saveLesson()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog>\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i3$2.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lessonInput", "lessonIdInput", "settings"], outputs: ["wordClicked"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i5$1.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "ngmodule", type: // Add the metadata editor here
2460
- DialogModule }, { kind: "component", type: i7.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: DcExtensionsViewerComponent, selector: "dc-extensions-viewer", inputs: ["data"] }, { kind: "component", type: DcLearnableViewerComponent, selector: "dc-learnable-viewer", inputs: ["data"] }, { kind: "component", type: DcAuditableViewerComponent, selector: "dc-auditable-viewer", inputs: ["data"] }, { kind: "component", type: DcManageableViewerComponent, selector: "dc-manageable-viewer", inputs: ["data"] }, { kind: "component", type: DcReactionsViewerComponent, selector: "dc-reactions-viewer", inputs: ["data"] }, { kind: "component", type: AssetsLoaderComponent, selector: "assets-loader", inputs: ["assets", "storagePath"], outputs: ["assetsChange", "assetUpdate", "onFileSelected"] }] }); }
2373
+ onAssetUpdate(event) {
2374
+ this.entityCommunicationService.partialUpdate(this.entityId(), { assets: event.assets });
2375
+ }
2376
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2377
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], usesInheritance: true, ngImport: i0, template: "<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader\n [assets]=\"entity()?.assets\"\n storagePath=\"lessons/{{ entityId() }}\"\n (assetsChange)=\"onAssetsChange($event)\"\n (assetUpdate)=\"onAssetUpdate($event)\"></assets-loader>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"entity()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"entity()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-form [form]=\"form.controls.manageable\"></dc-manageable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-form [form]=\"form.controls.learnable\"></dc-learnable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"entity()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"entity()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<div [formGroup]=\"form\">\n <p-selectButton [options]=\"formatOptions\" formControlName=\"format\" optionLabel=\"label\" optionValue=\"value\" />\n</div>\n<dc-lesson-metadata-editor [lesson]=\"entity()\" [form]=\"form\" [isLoadingLesson]=\"isLoadingLesson()\"></dc-lesson-metadata-editor>\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"htmlTemporal\"\n (ngModelChange)=\"updateHtmlTextCoded('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"entity()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"save()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<!-- <p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog> -->\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}::ng-deep .p-splitter .p-splitterpanel{overflow:auto!important}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i3$1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lessonInput", "lessonIdInput", "settings"], outputs: ["wordClicked"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i5$1.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i6$1.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "ngmodule", type: // Add the component adder here
2378
+ DialogModule }, { kind: "component", type: DcExtensionsViewerComponent, selector: "dc-extensions-viewer", inputs: ["data"] }, { kind: "component", type: DcLearnableViewerComponent, selector: "dc-learnable-viewer", inputs: ["data"] }, { kind: "component", type: DcAuditableViewerComponent, selector: "dc-auditable-viewer", inputs: ["data"] }, { kind: "component", type: DcReactionsViewerComponent, selector: "dc-reactions-viewer", inputs: ["data"] }, { kind: "component", type: AssetsLoaderComponent, selector: "assets-loader", inputs: ["assets", "storagePath"], outputs: ["assetsChange", "assetUpdate", "onFileSelected"] }, { kind: "component", type: DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["form", "lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest"] }, { kind: "component", type: DcManageableFormComponent, selector: "dc-manageable-form", inputs: ["form"] }, { kind: "component", type: DcLearnableFormComponent, selector: "dc-learnable-form", inputs: ["form"] }] }); }
2461
2379
  }
2462
2380
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
2463
2381
  type: Component,
2464
2382
  args: [{ selector: 'dc-lesson-editor', standalone: true, imports: [
2465
2383
  ButtonModule,
2466
2384
  CKEditorModule,
2467
- CropperComponentModal,
2468
2385
  DCLessonRendererComponent,
2469
2386
  FormsModule,
2470
2387
  InputTextModule,
2471
2388
  ReactiveFormsModule,
2389
+ SelectButtonModule,
2472
2390
  SplitterModule,
2473
2391
  TooltipModule,
2474
2392
  DCLessonComponentAdderComponent, // Add the component adder here
2475
- DCLessonMetadataEditorComponent, // Add the metadata editor here
2476
2393
  DialogModule,
2477
2394
  DcExtensionsViewerComponent,
2478
2395
  DcLearnableViewerComponent,
2479
2396
  DcAuditableViewerComponent,
2480
- DcManageableViewerComponent,
2481
2397
  DcReactionsViewerComponent,
2482
2398
  AssetsLoaderComponent,
2483
- ], providers: [LessonNotionService], template: "<!-- <div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n\n <p-button\n (click)=\"generateBanner()\"\n class=\"generate-banner-btn\"\n icon=\"pi pi-sparkles\"\n severity=\"primary\"\n [rounded]=\"true\"\n [raised]=\"true\"\n pTooltip=\"Generar Banner AI\"\n tooltipPosition=\"left\"></p-button>\n\n <p-button class=\"prompt-visual\" icon=\"pi pi-info\" label=\"Ver Prompts\" [link]=\"true\" (click)=\"showPrompts()\" />\n</div> -->\n\n<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader [assets]=\"lesson()?.assets\" storagePath=\"lessons/{{ lesson()?.id }}\" (assetsChange)=\"onAssetsChange($event)\"></assets-loader>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"lesson()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"lesson()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-viewer [data]=\"lesson()?.manageable\"></dc-manageable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"lesson()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"lesson()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<!-- <dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [lessonForm]=\"lessonForm\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\">\n</dc-lesson-metadata-editor> -->\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"lesson()?.textCoded\"\n (ngModelChange)=\"updateLessonProperty('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"lesson()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <!-- Removed p-speeddial -->\n <p-button icon=\"pi pi-save\" (click)=\"saveLesson()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog>\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}\n"] }]
2484
- }], ctorParameters: () => [], propDecorators: { target: [{
2485
- type: ViewChild,
2486
- args: ['target', { read: ViewContainerRef }]
2487
- }], dhtml: [{
2488
- type: ViewChild,
2489
- args: ['dhtml', { static: true }]
2490
- }] } });
2399
+ DCLessonMetadataEditorComponent,
2400
+ DcManageableFormComponent,
2401
+ DcLearnableFormComponent,
2402
+ ], providers: [LessonNotionService], template: "<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader\n [assets]=\"entity()?.assets\"\n storagePath=\"lessons/{{ entityId() }}\"\n (assetsChange)=\"onAssetsChange($event)\"\n (assetUpdate)=\"onAssetUpdate($event)\"></assets-loader>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"entity()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"entity()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-form [form]=\"form.controls.manageable\"></dc-manageable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-form [form]=\"form.controls.learnable\"></dc-learnable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"entity()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"entity()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<div [formGroup]=\"form\">\n <p-selectButton [options]=\"formatOptions\" formControlName=\"format\" optionLabel=\"label\" optionValue=\"value\" />\n</div>\n<dc-lesson-metadata-editor [lesson]=\"entity()\" [form]=\"form\" [isLoadingLesson]=\"isLoadingLesson()\"></dc-lesson-metadata-editor>\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"htmlTemporal\"\n (ngModelChange)=\"updateHtmlTextCoded('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"entity()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"save()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<!-- <p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog> -->\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}::ng-deep .p-splitter .p-splitterpanel{overflow:auto!important}\n"] }]
2403
+ }] });
2404
+
2405
+ class LessonsV2Component {
2406
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonsV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2407
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: LessonsV2Component, isStandalone: true, selector: "app-lessonsv2", ngImport: i0, template: '<router-outlet></router-outlet>', isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
2408
+ }
2409
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonsV2Component, decorators: [{
2410
+ type: Component,
2411
+ args: [{
2412
+ selector: 'app-lessonsv2',
2413
+ template: '<router-outlet></router-outlet>',
2414
+ standalone: true,
2415
+ imports: [RouterOutlet],
2416
+ }]
2417
+ }] });
2491
2418
 
2492
2419
  // This is the base class for all the components that are going to be used in the lessons.
2493
2420
  class LessonDynamicComponent {
@@ -2549,6 +2476,12 @@ class CourseService extends EntityCommunicationService {
2549
2476
  constructor() {
2550
2477
  super('courses');
2551
2478
  }
2479
+ generateLanguageCourse(base, target, id = '') {
2480
+ return this.httpService.post(`api/courses/generate-language`, { base, target, id });
2481
+ }
2482
+ autogenerateLessons(id) {
2483
+ return this.httpService.post(`api/courses/autogenerate-lessons`, { id });
2484
+ }
2552
2485
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2553
2486
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseService, providedIn: 'root' }); }
2554
2487
  }
@@ -2565,6 +2498,7 @@ class CourseListComponent extends PaginationBase {
2565
2498
  // Services
2566
2499
  this.toastService = inject(TOAST_ALERTS_TOKEN);
2567
2500
  this.courseService = inject(CourseService);
2501
+ this.userService = inject(UserService);
2568
2502
  this.cdr = inject(ChangeDetectorRef);
2569
2503
  // Inputs
2570
2504
  this.viewType = 'card';
@@ -2595,8 +2529,13 @@ class CourseListComponent extends PaginationBase {
2595
2529
  ];
2596
2530
  }
2597
2531
  async ngOnInit() {
2598
- this.filterConfig.returnProps = { _id: 1, id: 1, name: 1, description: 1, updatedAt: 1, image: 1 };
2599
- this.filterConfig.filters = { targetLang: 'de', baseLang: 'es' };
2532
+ const user = this.userService.user();
2533
+ const targetLang = user?.settings?.['targetLanguage'];
2534
+ const baseLang = user?.settings?.['baseLanguage'];
2535
+ // console.log(user?.settings?.['targetLanguage']);
2536
+ this.filterConfig.filters = { baseLang: baseLang, targetLang: targetLang };
2537
+ this.filterConfig.returnProps = { _id: 1, id: 1, name: 1, description: 1, updatedAt: 1, image: 1, baseLang: 1, targetLang: 1, auditable: 1 };
2538
+ // this.filterConfig.filters = { targetLang: 'de', baseLang: 'es' };
2600
2539
  const response = await this.courseService.query(this.filterConfig);
2601
2540
  this.courses.set(response.rows);
2602
2541
  this.cdr.detectChanges();
@@ -2647,7 +2586,7 @@ class CourseListComponent extends PaginationBase {
2647
2586
  }
2648
2587
  }
2649
2588
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseListComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2650
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseListComponent, isStandalone: true, selector: "app-course-list", inputs: { viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: false, isRequired: false, transformFunction: null }, onlyView: { classPropertyName: "onlyView", publicName: "onlyView", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, usesInheritance: true, ngImport: i0, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n\n />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course.updatedAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin-top:10px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin-bottom:10px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"], dependencies: [{ kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$2.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: SlicePipe, name: "slice" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2589
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseListComponent, isStandalone: true, selector: "app-course-list", inputs: { viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: false, isRequired: false, transformFunction: null }, onlyView: { classPropertyName: "onlyView", publicName: "onlyView", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, usesInheritance: true, ngImport: i0, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course?.auditable?.createdAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n\n <p-tag severity=\"success\" [value]=\"course.baseLang | langDesc : 'es'\" [rounded]=\"true\" />\n ->\n <p-tag severity=\"info\" [value]=\"course.targetLang | langDesc : 'es'\" [rounded]=\"true\" />\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin:20px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin:20px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"], dependencies: [{ kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: LangDescTranslation, name: "langDesc" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2651
2590
  }
2652
2591
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseListComponent, decorators: [{
2653
2592
  type: Component,
@@ -2662,24 +2601,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
2662
2601
  RouterModule,
2663
2602
  TableModule,
2664
2603
  QuickTableComponent,
2665
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n\n />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course.updatedAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin-top:10px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin-bottom:10px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"] }]
2604
+ TagModule,
2605
+ LangDescTranslation,
2606
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course?.auditable?.createdAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n\n <p-tag severity=\"success\" [value]=\"course.baseLang | langDesc : 'es'\" [rounded]=\"true\" />\n ->\n <p-tag severity=\"info\" [value]=\"course.targetLang | langDesc : 'es'\" [rounded]=\"true\" />\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin:20px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin:20px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"] }]
2666
2607
  }], propDecorators: { viewType: [{
2667
2608
  type: Input
2668
2609
  }] } });
2669
2610
 
2670
2611
  class CourseDetailComponent {
2612
+ constructor() {
2613
+ this.entityCommunicationService = inject(CourseService);
2614
+ this.activatedRoute = inject(ActivatedRoute);
2615
+ this.messageService = inject(MessageService);
2616
+ this.courseId = this.activatedRoute.snapshot.paramMap.get('id');
2617
+ this.course = signal(null, ...(ngDevMode ? [{ debugName: "course" }] : []));
2618
+ this.generatingLessons = signal(false, ...(ngDevMode ? [{ debugName: "generatingLessons" }] : []));
2619
+ this.generatingLesson = signal('', ...(ngDevMode ? [{ debugName: "generatingLesson" }] : []));
2620
+ }
2621
+ ngOnInit() {
2622
+ this.loadCourse();
2623
+ }
2624
+ async generatePendingLessons() {
2625
+ this.generatingLessons.set(true);
2626
+ try {
2627
+ await this.entityCommunicationService.autogenerateLessons(this.courseId);
2628
+ this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Lessons generated successfully' });
2629
+ this.loadCourse();
2630
+ }
2631
+ catch (error) {
2632
+ this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error generating lessons' });
2633
+ }
2634
+ finally {
2635
+ this.generatingLessons.set(false);
2636
+ }
2637
+ }
2638
+ async generateLesson(lessonId) {
2639
+ this.generatingLesson.set(lessonId);
2640
+ try {
2641
+ // TODO: Implement the generateLesson method
2642
+ // await this.entityCommunicationService.autogenerateLesson(this.courseId, lessonId);
2643
+ this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Lesson generated successfully' });
2644
+ this.loadCourse();
2645
+ }
2646
+ catch (error) {
2647
+ this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error generating lesson' });
2648
+ }
2649
+ finally {
2650
+ this.generatingLesson.set('');
2651
+ }
2652
+ }
2653
+ async loadCourse() {
2654
+ const course = await this.entityCommunicationService.findOne(this.courseId);
2655
+ this.course.set(course);
2656
+ }
2671
2657
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2672
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CourseDetailComponent, isStandalone: true, selector: "app-course-detail", ngImport: i0, template: `<p>course-detail works!</p>`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2658
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseDetailComponent, isStandalone: true, selector: "app-course-detail", providers: [MessageService], ngImport: i0, template: "@if (course(); as course) {\n<div class=\"course-detail-container p-4\">\n <p-card>\n <ng-template pTemplate=\"title\">\n <div class=\"flex justify-content-between align-items-center\">\n <span>{{ course.name }}</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"subtitle\">\n <div class=\"flex align-items-center gap-2\">\n <i class=\"pi pi-book\"></i>\n <span>{{ course.moduleCount }} Modules / {{ course.totalLessons }} Lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ course.description }}</p>\n\n <div class=\"grid mt-3\">\n @for (module of course.modules; track module.id) {\n <div class=\"col-12 md:col-6\">\n <p-panel [toggleable]=\"true\">\n <ng-template pTemplate=\"header\">\n <div class=\"flex align-items-center gap-2 w-full\">\n <i class=\"pi pi-list\"></i>\n <span class=\"font-bold white-space-nowrap\">{{ module.title }}</span>\n <span class=\"text-sm text-color-secondary ml-auto\">{{ module.lessonCount }} lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ module.description }}</p>\n <ul class=\"list-none p-0 m-0 lessons-list\">\n @for (lesson of module.lessons; track lesson.id) {\n <li class=\"flex align-items-center justify-content-between p-2 border-bottom-1 surface-border\">\n <div class=\"flex align-items-center gap-2\">\n @if(lesson.generated) {\n <i class=\"pi pi-play-circle text-green-500\"></i>\n } @else {\n <i class=\"pi pi-lock text-gray-500\"></i>\n }\n <span>{{ lesson.title }}</span>\n </div>\n @if(lesson.generated) {\n <p-button icon=\"pi pi-chevron-right\" [text]=\"true\" [rounded]=\"true\"></p-button>\n } @else {\n <p-button label=\"Generate\" icon=\"pi pi-cog\" [text]=\"true\" size=\"small\"></p-button>\n }\n </li>\n }\n </ul>\n </ng-template>\n </p-panel>\n </div>\n }\n </div>\n </ng-template>\n <ng-template pTemplate=\"footer\">\n <div class=\"flex justify-content-end\">\n <p-button label=\"Generate Pending Lessons\" icon=\"pi pi-cogs\" (onClick)=\"generatePendingLessons()\" [loading]=\"generatingLessons()\"></p-button>\n </div>\n </ng-template>\n </p-card>\n</div>\n} @else {\n<div class=\"flex justify-content-center align-items-center h-full\">\n <p-progressSpinner></p-progressSpinner>\n</div>\n}\n", styles: [":host{display:block;height:100%}.course-detail-container{max-width:960px;margin:auto}.lessons-list li:last-child{border-bottom:none!important}.modules-container{display:flex;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:1rem}.modules-container .p-panel{flex:0 0 48%;max-width:48%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i3$2.Panel, selector: "p-panel", inputs: ["toggleable", "header", "collapsed", "id", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i5$2.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: ToastModule }, { kind: "ngmodule", type: TagModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2673
2659
  }
2674
2660
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseDetailComponent, decorators: [{
2675
2661
  type: Component,
2676
- args: [{ selector: 'app-course-detail', imports: [], template: `<p>course-detail works!</p>`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
2662
+ args: [{ selector: 'app-course-detail', imports: [CommonModule, CardModule, PanelModule, ButtonModule, ProgressSpinnerModule, ToastModule, TagModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [MessageService], template: "@if (course(); as course) {\n<div class=\"course-detail-container p-4\">\n <p-card>\n <ng-template pTemplate=\"title\">\n <div class=\"flex justify-content-between align-items-center\">\n <span>{{ course.name }}</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"subtitle\">\n <div class=\"flex align-items-center gap-2\">\n <i class=\"pi pi-book\"></i>\n <span>{{ course.moduleCount }} Modules / {{ course.totalLessons }} Lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ course.description }}</p>\n\n <div class=\"grid mt-3\">\n @for (module of course.modules; track module.id) {\n <div class=\"col-12 md:col-6\">\n <p-panel [toggleable]=\"true\">\n <ng-template pTemplate=\"header\">\n <div class=\"flex align-items-center gap-2 w-full\">\n <i class=\"pi pi-list\"></i>\n <span class=\"font-bold white-space-nowrap\">{{ module.title }}</span>\n <span class=\"text-sm text-color-secondary ml-auto\">{{ module.lessonCount }} lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ module.description }}</p>\n <ul class=\"list-none p-0 m-0 lessons-list\">\n @for (lesson of module.lessons; track lesson.id) {\n <li class=\"flex align-items-center justify-content-between p-2 border-bottom-1 surface-border\">\n <div class=\"flex align-items-center gap-2\">\n @if(lesson.generated) {\n <i class=\"pi pi-play-circle text-green-500\"></i>\n } @else {\n <i class=\"pi pi-lock text-gray-500\"></i>\n }\n <span>{{ lesson.title }}</span>\n </div>\n @if(lesson.generated) {\n <p-button icon=\"pi pi-chevron-right\" [text]=\"true\" [rounded]=\"true\"></p-button>\n } @else {\n <p-button label=\"Generate\" icon=\"pi pi-cog\" [text]=\"true\" size=\"small\"></p-button>\n }\n </li>\n }\n </ul>\n </ng-template>\n </p-panel>\n </div>\n }\n </div>\n </ng-template>\n <ng-template pTemplate=\"footer\">\n <div class=\"flex justify-content-end\">\n <p-button label=\"Generate Pending Lessons\" icon=\"pi pi-cogs\" (onClick)=\"generatePendingLessons()\" [loading]=\"generatingLessons()\"></p-button>\n </div>\n </ng-template>\n </p-card>\n</div>\n} @else {\n<div class=\"flex justify-content-center align-items-center h-full\">\n <p-progressSpinner></p-progressSpinner>\n</div>\n}\n", styles: [":host{display:block;height:100%}.course-detail-container{max-width:960px;margin:auto}.lessons-list li:last-child{border-bottom:none!important}.modules-container{display:flex;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:1rem}.modules-container .p-panel{flex:0 0 48%;max-width:48%}\n"] }]
2677
2663
  }] });
2678
2664
 
2679
2665
  class CourseFormComponent extends EntityBaseFormComponent {
2680
2666
  constructor() {
2681
- super(...arguments);
2667
+ super();
2682
2668
  this.entityCommunicationService = inject(CourseService);
2669
+ this.loadingBarService = inject(LoadingBarService);
2683
2670
  this.fb = inject(FormBuilder);
2684
2671
  this.languageOptions = getSupportedLanguageOptions('en');
2685
2672
  this.form = this.fb.group({
@@ -2698,11 +2685,15 @@ class CourseFormComponent extends EntityBaseFormComponent {
2698
2685
  { key: 'title', type: 'input', props: { label: 'Title', placeholder: 'Title', required: false } },
2699
2686
  { key: 'content', type: 'textarea', props: { label: 'Content', placeholder: 'Content', required: false } },
2700
2687
  ];
2688
+ effect(() => {
2689
+ console.log(this.entity());
2690
+ });
2701
2691
  }
2702
2692
  patchForm(entity) {
2703
2693
  this.form.patchValue(entity);
2704
2694
  }
2705
2695
  async save() {
2696
+ this.toastService.success({ title: 'Course', subtitle: 'Data was saved' });
2706
2697
  const result = await super.save();
2707
2698
  if (result) {
2708
2699
  this.onSave.emit(result);
@@ -2715,8 +2706,21 @@ class CourseFormComponent extends EntityBaseFormComponent {
2715
2706
  handleImageUpload(event) {
2716
2707
  this.form.patchValue({ image: event });
2717
2708
  }
2718
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseFormComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2719
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CourseFormComponent, isStandalone: true, selector: "app-source-form", outputs: { onSave: "onSave" }, usesInheritance: true, ngImport: i0, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3$3.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "ngmodule", type: DialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2709
+ async generateCourse() {
2710
+ const { baseLang, targetLang } = this.form.value;
2711
+ if (baseLang && targetLang) {
2712
+ this.toastService.info({ title: 'Course Generating', subtitle: 'Este proceso puede tomar unos minutos' });
2713
+ this.loadingBarService.showIndeterminate();
2714
+ const course = await this.entityCommunicationService.generateLanguageCourse(baseLang, targetLang, this.entity()?.id);
2715
+ this.form.patchValue(course);
2716
+ if (this.toastService) {
2717
+ this.toastService.success({ title: 'Course Generated', subtitle: 'Course content has been generated successfully.' });
2718
+ }
2719
+ this.loadingBarService.successAndHide();
2720
+ }
2721
+ }
2722
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2723
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseFormComponent, isStandalone: true, selector: "app-source-form", outputs: { onSave: "onSave" }, usesInheritance: true, ngImport: i0, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div>\n @if (entity()?.modules?.length > 0) {\n <p-message severity=\"info\"\n >El curso ya tiene modulos,\n\n <a routerLink=\"../../details/{{ entity()?.id }}\"> <p-button icon=\"pi pi-eye\" label=\"Ver a los detalles\" [link]=\"true\"></p-button></a>\n </p-message>\n } @else {\n <p-message severity=\"warn\">Este curso no tiene modulos intentega generalos abajo.</p-message>\n }\n </div>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button\n (click)=\"generateCourse()\"\n label=\"Generate Course\"\n [disabled]=\"!form.controls['baseLang'].valid || !form.controls['targetLang'].valid\"\n icon=\"pi pi-sparkles\"\n iconPos=\"right\"\n styleClass=\"p-button-secondary\"></p-button>\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3$3.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i4.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2720
2724
  }
2721
2725
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseFormComponent, decorators: [{
2722
2726
  type: Component,
@@ -2733,8 +2737,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
2733
2737
  FormlyModule,
2734
2738
  DialogModule,
2735
2739
  CourseListComponent,
2736
- ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"] }]
2737
- }] });
2740
+ MessageModule,
2741
+ RouterLink,
2742
+ ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div>\n @if (entity()?.modules?.length > 0) {\n <p-message severity=\"info\"\n >El curso ya tiene modulos,\n\n <a routerLink=\"../../details/{{ entity()?.id }}\"> <p-button icon=\"pi pi-eye\" label=\"Ver a los detalles\" [link]=\"true\"></p-button></a>\n </p-message>\n } @else {\n <p-message severity=\"warn\">Este curso no tiene modulos intentega generalos abajo.</p-message>\n }\n </div>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button\n (click)=\"generateCourse()\"\n label=\"Generate Course\"\n [disabled]=\"!form.controls['baseLang'].valid || !form.controls['targetLang'].valid\"\n icon=\"pi pi-sparkles\"\n iconPos=\"right\"\n styleClass=\"p-button-secondary\"></p-button>\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"] }]
2743
+ }], ctorParameters: () => [] });
2738
2744
 
2739
2745
  const COURSES_ROUTES = [
2740
2746
  {
@@ -2764,7 +2770,7 @@ const COURSES_ROUTES = [
2764
2770
  class CoursesComponent {
2765
2771
  static { this.routes = COURSES_ROUTES; }
2766
2772
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2767
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CoursesComponent, isStandalone: true, selector: "app-courses", ngImport: i0, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2773
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CoursesComponent, isStandalone: true, selector: "app-courses", ngImport: i0, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$4.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2768
2774
  }
2769
2775
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesComponent, decorators: [{
2770
2776
  type: Component,
@@ -2779,5 +2785,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImpor
2779
2785
  * Generated bundle index. Do not edit.
2780
2786
  */
2781
2787
 
2782
- export { ComponentBuilder, ComponentWithForm, CourseDetailComponent, CourseFormComponent, CourseListComponent, CourseService, CoursesAdminComponent, CoursesComponent, CoursesService, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, DefaultLessonsService, DynamicComponentBuilders, DynamicComponents, DynamicComponentsService, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LangDescTranslationPipe, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonDynamicComponent, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLanguageSimpleAgent, getLessonComponentClass, provideLessonsService, provideNotionService };
2788
+ export { ComponentBuilder, ComponentWithForm, CourseDetailComponent, CourseFormComponent, CourseListComponent, CourseService, CoursesAdminComponent, CoursesComponent, CoursesService, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, DefaultLessonsService, DynamicComponentBuilders, DynamicComponents, DynamicComponentsService, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonConversationService, LessonDynamicComponent, LessonRendererService, LessonsV2Component, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLanguageSimpleAgent, getLessonComponentClass, provideLessonsService, provideNotionService };
2783
2789
  //# sourceMappingURL=dataclouder-ngx-lessons.mjs.map