@dataclouder/ngx-lessons 0.0.29 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/fesm2022/dataclouder-ngx-lessons.mjs +1205 -514
  2. package/fesm2022/dataclouder-ngx-lessons.mjs.map +1 -1
  3. package/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.d.ts +6 -8
  4. package/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.d.ts +11 -0
  5. package/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.d.ts +39 -38
  6. package/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.d.ts +22 -0
  7. package/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.d.ts +32 -37
  8. package/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.d.ts +7 -17
  9. package/lib/components/lesson-mini-components/components/ComponentBuilder.d.ts +7 -2
  10. package/lib/components/lesson-mini-components/components/lessons.clases.d.ts +17 -42
  11. package/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.d.ts +13 -0
  12. package/lib/components/lesson-mini-components/components/speaker/speaker.component.d.ts +12 -0
  13. package/lib/services/lesson-ai.service.d.ts +18 -0
  14. package/lib/services/lesson-notion.service.d.ts +35 -0
  15. package/lib/services/lesson-utils.service.d.ts +34 -0
  16. package/package.json +3 -2
  17. package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.html +40 -35
  18. package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.scss +15 -2
  19. package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.ts +16 -18
  20. package/src/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.css +1 -0
  21. package/src/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.html +46 -0
  22. package/src/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.ts +52 -0
  23. package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.html +54 -92
  24. package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.ts +268 -230
  25. package/src/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.css +1 -0
  26. package/src/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.html +72 -0
  27. package/src/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.ts +60 -0
  28. package/src/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.html +23 -27
  29. package/src/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.ts +247 -186
  30. package/src/lib/components/dc-lessons/lesson-form/lesson-form.component.ts +2 -2
  31. package/src/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.html +3 -3
  32. package/src/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.ts +28 -46
  33. package/src/lib/components/lesson-mini-components/components/ComponentBuilder.ts +23 -15
  34. package/src/lib/components/lesson-mini-components/components/lessons.clases.ts +32 -66
  35. package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.html +62 -58
  36. package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.ts +2 -2
  37. package/src/lib/components/lesson-mini-components/components/selector/selector.component.html +1 -2
  38. package/src/lib/components/lesson-mini-components/components/selector/selector.component.ts +2 -2
  39. package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.html +5 -27
  40. package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.ts +38 -25
  41. package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.html +9 -7
  42. package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.ts +30 -26
  43. package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcher.component.ts +2 -2
  44. package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcherBuilder/translationSwitcherBuilder.component.ts +2 -2
  45. package/src/lib/services/lesson-ai.service.ts +103 -0
  46. package/src/lib/services/lesson-notion.service.ts +161 -0
  47. package/src/lib/services/lesson-utils.service.ts +181 -0
  48. package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.spec.ts +0 -25
  49. package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.spec.ts +0 -25
  50. package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.spec.ts +0 -25
@@ -1,47 +1,49 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, Input, ChangeDetectionStrategy, InjectionToken, Pipe, EventEmitter, Output, Inject, ViewChildren, ViewChild, ViewContainerRef } from '@angular/core';
2
+ import { Component, Input, ChangeDetectionStrategy, signal, InjectionToken, Pipe, EventEmitter, Output, ViewChildren, Inject, inject, Injectable, input, Renderer2, ViewContainerRef, computed, effect, ViewChild } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
- import { FormControl, UntypedFormControl, FormsModule, ReactiveFormsModule, Validators, UntypedFormGroup } from '@angular/forms';
5
- import * as i2$3 from '@angular/common';
6
- import { NgFor, NgIf, CommonModule, DatePipe, NgComponentOutlet, KeyValuePipe } from '@angular/common';
4
+ import { FormControl, UntypedFormControl, FormsModule, ReactiveFormsModule, Validators, FormGroup } from '@angular/forms';
7
5
  import * as i4 from 'primeng/inputtext';
8
6
  import { InputTextModule } from 'primeng/inputtext';
9
- import * as i3 from 'primeng/button';
7
+ import * as i1$1 from 'primeng/button';
10
8
  import { ButtonModule } from 'primeng/button';
11
9
  import * as i5 from 'primeng/message';
12
10
  import { MessageModule } from 'primeng/message';
11
+ import { nanoid } from 'nanoid';
13
12
  import * as i2 from 'primeng/dynamicdialog';
14
- import * as i2$1 from 'primeng/dropdown';
13
+ import { DialogService } from 'primeng/dynamicdialog';
14
+ import * as i2$1 from 'primeng/select';
15
+ import { SelectModule } from 'primeng/select';
15
16
  import { DropdownModule } from 'primeng/dropdown';
16
- import * as i3$2 from 'primeng/paginator';
17
+ import { NgxTtsComponent } from '@dataclouder/ngx-tts';
18
+ import * as i2$3 from 'primeng/paginator';
17
19
  import { PaginatorModule } from 'primeng/paginator';
18
- import * as i1$1 from '@angular/router';
19
- import { RouterModule } from '@angular/router';
20
- import * as i5$1 from '@dataclouder/ngx-core';
20
+ import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule, JsonPipe } from '@angular/common';
21
+ import * as i1$2 from '@angular/router';
22
+ import { RouterModule, ActivatedRoute, Router } from '@angular/router';
23
+ import * as i4$1 from '@dataclouder/ngx-core';
21
24
  import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
22
25
  import { PopoverModule } from 'primeng/popover';
23
26
  import * as i2$2 from 'primeng/speeddial';
24
27
  import { SpeedDialModule } from 'primeng/speeddial';
25
- import * as i3$1 from 'primeng/card';
28
+ import * as i3 from 'primeng/card';
26
29
  import { CardModule } from 'primeng/card';
30
+ import { toSignal } from '@angular/core/rxjs-interop';
31
+ import { map } from 'rxjs';
27
32
  import BalloonEditor from '@ckeditor/ckeditor5-build-balloon-block';
28
- import * as i4$2 from '@ckeditor/ckeditor5-angular';
33
+ import * as i3$1 from '@ckeditor/ckeditor5-angular';
29
34
  import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
30
- import * as i8 from 'primeng/splitter';
35
+ import * as i5$1 from 'primeng/splitter';
31
36
  import { SplitterModule } from 'primeng/splitter';
32
- import * as i9 from 'primeng/tooltip';
37
+ import * as i2$5 from 'primeng/tooltip';
33
38
  import { TooltipModule } from 'primeng/tooltip';
34
- import { AspectType, ResolutionType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
35
- import * as i2$4 from '@dataclouder/ngx-agent-cards';
36
- import { ConversationType, TextEngines, CONVERSATION_AI_TOKEN, DCChatComponent } from '@dataclouder/ngx-agent-cards';
37
- import * as i4$1 from 'primeng/drawer';
39
+ import { ResolutionType, AspectType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
40
+ import { TextEngines, ConversationType, DCChatComponent } from '@dataclouder/ngx-agent-cards';
41
+ import * as i2$4 from 'primeng/drawer';
38
42
  import { DrawerModule } from 'primeng/drawer';
39
- import * as i6 from 'primeng/api';
43
+ import * as i2$6 from 'primeng/api';
40
44
 
41
45
  class ComponentBuilder {
42
- constructor(formBuilder,
43
- // protected toastrService: ToastService, // protected ref: NbDialogRef<ComponentBuilder>,
44
- ref) {
46
+ constructor(formBuilder, ref) {
45
47
  this.formBuilder = formBuilder;
46
48
  this.ref = ref;
47
49
  // Sobreescribir si es necesario
@@ -74,25 +76,30 @@ class ComponentBuilder {
74
76
  delete data[key];
75
77
  }
76
78
  });
77
- const id = Math.random().toString(36).substring(7);
78
- const code = { id: id, component: this.questionType, settings: { ...data } };
79
- return `~${JSON.stringify(code)}~`;
79
+ const code = { component: this.questionType, settings: { ...data } };
80
+ return code;
80
81
  }
81
82
  showCode() {
82
83
  const code = this.getCode();
83
- alert(code);
84
+ const stringCode = `~${JSON.stringify(code)}~`;
85
+ alert(stringCode);
84
86
  }
85
- async copyToClipboard() {
87
+ getComponentData() {
86
88
  const code = this.getCode();
87
- await navigator.clipboard.writeText(code);
88
- // this.toastrService.info('Control + V para pegar', 'Se copió al portapapeles');
89
- // this.ref.close();
90
- this.ref.close(code);
89
+ const id = nanoid().replace(/-/g, '_');
90
+ code.id = id;
91
+ return { str: `~${JSON.stringify(code)}~`, obj: code };
91
92
  }
92
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ComponentBuilder, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
93
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ComponentBuilder, isStandalone: true, selector: "app-component-builder", ngImport: i0, template: '<div>no template</div>', isInline: true }); }
93
+ // TODO: for now copyToClipboard is the that that closes modal and return data.
94
+ async copyToClipboard() {
95
+ const data = this.getComponentData();
96
+ await navigator.clipboard.writeText(data.str);
97
+ this.ref.close(data);
98
+ }
99
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ComponentBuilder, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
100
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: ComponentBuilder, isStandalone: true, selector: "app-component-builder", ngImport: i0, template: '<div>no template</div>', isInline: true }); }
94
101
  }
95
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ComponentBuilder, decorators: [{
102
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ComponentBuilder, decorators: [{
96
103
  type: Component,
97
104
  args: [{
98
105
  selector: 'app-component-builder',
@@ -113,10 +120,10 @@ class ComponentWithForm {
113
120
  validate() {
114
121
  // TODO: generic method to evaluate
115
122
  }
116
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ComponentWithForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
117
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ComponentWithForm, isStandalone: true, selector: "app-component-form", ngImport: i0, template: '<div>no template</div>', isInline: true }); }
123
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ComponentWithForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
124
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: ComponentWithForm, isStandalone: true, selector: "app-component-form", ngImport: i0, template: '<div>no template</div>', isInline: true }); }
118
125
  }
119
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ComponentWithForm, decorators: [{
126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ComponentWithForm, decorators: [{
120
127
  type: Component,
121
128
  args: [{
122
129
  selector: 'app-component-form',
@@ -152,12 +159,12 @@ class SelectorComponent extends ComponentWithForm {
152
159
  }
153
160
  return true;
154
161
  }
155
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
156
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: SelectorComponent, isStandalone: true, selector: "app-selector", inputs: { config: "config" }, usesInheritance: true, ngImport: i0, template: "<p-dropdown [class]=\"status\" placeholder=\"Selecciona\" [options]=\"config.settings.options\" [formControl]=\"control\"\r\n [showClear]=\"true\"></p-dropdown>", styles: ["::ng-deep .comp-selector button{min-width:80px}.warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i2$1.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }] }); }
162
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
163
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: SelectorComponent, isStandalone: true, selector: "app-selector", inputs: { config: "config" }, usesInheritance: true, ngImport: i0, template: "<p-select [class]=\"status\" placeholder=\"Selecciona\" [options]=\"config.settings.options\" [formControl]=\"control\"></p-select>\r\n", styles: ["::ng-deep .comp-selector button{min-width:80px}.warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }] }); }
157
164
  }
158
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SelectorComponent, decorators: [{
165
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectorComponent, decorators: [{
159
166
  type: Component,
160
- args: [{ selector: 'app-selector', standalone: true, imports: [FormsModule, ReactiveFormsModule, DropdownModule], template: "<p-dropdown [class]=\"status\" placeholder=\"Selecciona\" [options]=\"config.settings.options\" [formControl]=\"control\"\r\n [showClear]=\"true\"></p-dropdown>", styles: ["::ng-deep .comp-selector button{min-width:80px}.warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"] }]
167
+ args: [{ selector: 'app-selector', standalone: true, imports: [FormsModule, ReactiveFormsModule, SelectModule], template: "<p-select [class]=\"status\" placeholder=\"Selecciona\" [options]=\"config.settings.options\" [formControl]=\"control\"></p-select>\r\n", styles: ["::ng-deep .comp-selector button{min-width:80px}.warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"] }]
161
168
  }], ctorParameters: () => [], propDecorators: { config: [{
162
169
  type: Input
163
170
  }] } });
@@ -203,12 +210,12 @@ class SelectorBuilderComponent extends ComponentBuilder {
203
210
  get optionsForm() {
204
211
  return this.formGroup.get('options');
205
212
  }
206
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SelectorBuilderComponent, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
207
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: SelectorBuilderComponent, isStandalone: true, selector: "app-selector-builder", usesInheritance: true, ngImport: i0, template: "<div>\r\n <div>\r\n <p-message>Construcci\u00F3n del componente de Selecci\u00F3n, sirve para hacer una pregunta y mostrar varias opciones, ejemplo:</p-message>\r\n </div>\r\n\r\n <div>\r\n <span>En que a\u00F1o lleg\u00F3 cristobal colon a america?</span>\r\n <app-selector [config]=\"sampleConfig\"></app-selector>\r\n </div>\r\n\r\n <hr />\r\n\r\n <div>\r\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\r\n <input class=\"form-input\" type=\"text\" pInputText fullWidth formControlName=\"response\" placeholder=\"Respuesta Correcta...\" />\r\n <br />\r\n\r\n <input class=\"form-input\" type=\"\" pInputText fullWidth formControlName=\"hint\" placeholder=\"Escribe una pista para esta pregunta\" />\r\n\r\n <br />\r\n <input\r\n class=\"form-input\"\r\n type=\"text\"\r\n pInputText\r\n fullWidth\r\n formControlName=\"explanation\"\r\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\r\n\r\n <hr />\r\n <h6>Opciones</h6>\r\n\r\n <div class=\"form-group\" formArrayName=\"options\">\r\n <div\r\n style=\"display: flex; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 10px; flex-direction: column\"\r\n *ngFor=\"let item of optionsForm.controls; let i = index\">\r\n <div>\r\n <input type=\"text\" pInputText fullWidth [formControlName]=\"i\" />\r\n <p-button (click)=\"deleteFormArrayByIndex('options', i)\" icon=\"pi pi-times\" severity=\"danger\"></p-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <p-button (click)=\"pushControlToFormArray('options')\" label=\"Agregar Opci\u00F3n\" [text]=\"true\" severity=\"help\"></p-button>\r\n </form>\r\n\r\n <!-- <button nbButton (click)=\"isRendered = !isRendered\"> Renderizar </button> -->\r\n\r\n <div *ngIf=\"isRendered\">\r\n <!-- TODO: probably i need to pass some params -->\r\n <app-selector></app-selector>\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\r\n </div>\r\n</div>\r\n", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: "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: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SelectorComponent, selector: "app-selector", inputs: ["config"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i5.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }] }); }
213
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectorBuilderComponent, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
214
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: SelectorBuilderComponent, isStandalone: true, selector: "app-selector-builder", usesInheritance: true, ngImport: i0, template: "<div>\n <div>\n <p-message>Construcci\u00F3n del componente de Selecci\u00F3n, sirve para hacer una pregunta y mostrar varias opciones, ejemplo:</p-message>\n </div>\n\n <div>\n <span>En que a\u00F1o lleg\u00F3 cristobal colon a america?</span>\n <app-selector [config]=\"sampleConfig\"></app-selector>\n </div>\n\n <hr />\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input class=\"form-input\" type=\"text\" pInputText fullWidth formControlName=\"response\" placeholder=\"Respuesta Correcta...\" />\n <br />\n\n <input class=\"form-input\" type=\"\" pInputText fullWidth formControlName=\"hint\" placeholder=\"Escribe una pista para esta pregunta\" />\n\n <br />\n <input\n class=\"form-input\"\n type=\"text\"\n pInputText\n fullWidth\n formControlName=\"explanation\"\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\n\n <hr />\n <h6>Opciones</h6>\n\n <div class=\"form-group\" formArrayName=\"options\">\n @for (item of optionsForm.controls; track item; let i = $index) {\n <div\n style=\"display: flex; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 10px; flex-direction: column\"\n >\n <div>\n <input type=\"text\" pInputText fullWidth [formControlName]=\"i\" />\n <p-button (click)=\"deleteFormArrayByIndex('options', i)\" icon=\"pi pi-times\" severity=\"danger\"></p-button>\n </div>\n </div>\n }\n </div>\n\n <p-button (click)=\"pushControlToFormArray('options')\" label=\"Agregar Opci\u00F3n\" [text]=\"true\" severity=\"help\"></p-button>\n </form>\n\n <!-- <button nbButton (click)=\"isRendered = !isRendered\"> Renderizar </button> -->\n\n @if (isRendered) {\n <div>\n <!-- TODO: probably i need to pass some params -->\n <app-selector></app-selector>\n </div>\n }\n </div>\n\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n </div>\n", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: "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: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: SelectorComponent, selector: "app-selector", inputs: ["config"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i5.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }] }); }
208
215
  }
209
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SelectorBuilderComponent, decorators: [{
216
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SelectorBuilderComponent, decorators: [{
210
217
  type: Component,
211
- args: [{ selector: 'app-selector-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, NgFor, NgIf, SelectorComponent, InputTextModule, ButtonModule, MessageModule], template: "<div>\r\n <div>\r\n <p-message>Construcci\u00F3n del componente de Selecci\u00F3n, sirve para hacer una pregunta y mostrar varias opciones, ejemplo:</p-message>\r\n </div>\r\n\r\n <div>\r\n <span>En que a\u00F1o lleg\u00F3 cristobal colon a america?</span>\r\n <app-selector [config]=\"sampleConfig\"></app-selector>\r\n </div>\r\n\r\n <hr />\r\n\r\n <div>\r\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\r\n <input class=\"form-input\" type=\"text\" pInputText fullWidth formControlName=\"response\" placeholder=\"Respuesta Correcta...\" />\r\n <br />\r\n\r\n <input class=\"form-input\" type=\"\" pInputText fullWidth formControlName=\"hint\" placeholder=\"Escribe una pista para esta pregunta\" />\r\n\r\n <br />\r\n <input\r\n class=\"form-input\"\r\n type=\"text\"\r\n pInputText\r\n fullWidth\r\n formControlName=\"explanation\"\r\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\r\n\r\n <hr />\r\n <h6>Opciones</h6>\r\n\r\n <div class=\"form-group\" formArrayName=\"options\">\r\n <div\r\n style=\"display: flex; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 10px; flex-direction: column\"\r\n *ngFor=\"let item of optionsForm.controls; let i = index\">\r\n <div>\r\n <input type=\"text\" pInputText fullWidth [formControlName]=\"i\" />\r\n <p-button (click)=\"deleteFormArrayByIndex('options', i)\" icon=\"pi pi-times\" severity=\"danger\"></p-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <p-button (click)=\"pushControlToFormArray('options')\" label=\"Agregar Opci\u00F3n\" [text]=\"true\" severity=\"help\"></p-button>\r\n </form>\r\n\r\n <!-- <button nbButton (click)=\"isRendered = !isRendered\"> Renderizar </button> -->\r\n\r\n <div *ngIf=\"isRendered\">\r\n <!-- TODO: probably i need to pass some params -->\r\n <app-selector></app-selector>\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\r\n </div>\r\n</div>\r\n", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"] }]
218
+ args: [{ selector: 'app-selector-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, SelectorComponent, InputTextModule, ButtonModule, MessageModule], template: "<div>\n <div>\n <p-message>Construcci\u00F3n del componente de Selecci\u00F3n, sirve para hacer una pregunta y mostrar varias opciones, ejemplo:</p-message>\n </div>\n\n <div>\n <span>En que a\u00F1o lleg\u00F3 cristobal colon a america?</span>\n <app-selector [config]=\"sampleConfig\"></app-selector>\n </div>\n\n <hr />\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input class=\"form-input\" type=\"text\" pInputText fullWidth formControlName=\"response\" placeholder=\"Respuesta Correcta...\" />\n <br />\n\n <input class=\"form-input\" type=\"\" pInputText fullWidth formControlName=\"hint\" placeholder=\"Escribe una pista para esta pregunta\" />\n\n <br />\n <input\n class=\"form-input\"\n type=\"text\"\n pInputText\n fullWidth\n formControlName=\"explanation\"\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\n\n <hr />\n <h6>Opciones</h6>\n\n <div class=\"form-group\" formArrayName=\"options\">\n @for (item of optionsForm.controls; track item; let i = $index) {\n <div\n style=\"display: flex; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 10px; flex-direction: column\"\n >\n <div>\n <input type=\"text\" pInputText fullWidth [formControlName]=\"i\" />\n <p-button (click)=\"deleteFormArrayByIndex('options', i)\" icon=\"pi pi-times\" severity=\"danger\"></p-button>\n </div>\n </div>\n }\n </div>\n\n <p-button (click)=\"pushControlToFormArray('options')\" label=\"Agregar Opci\u00F3n\" [text]=\"true\" severity=\"help\"></p-button>\n </form>\n\n <!-- <button nbButton (click)=\"isRendered = !isRendered\"> Renderizar </button> -->\n\n @if (isRendered) {\n <div>\n <!-- TODO: probably i need to pass some params -->\n <app-selector></app-selector>\n </div>\n }\n </div>\n\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n </div>\n", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"] }]
212
219
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.DynamicDialogRef }] });
213
220
 
214
221
  class TextWriterBuiderComponent extends ComponentBuilder {
@@ -221,10 +228,10 @@ class TextWriterBuiderComponent extends ComponentBuilder {
221
228
  this.formGroup = this.formBuilder.group({ response: ['', Validators.required], hint: [], explanation: [] });
222
229
  }
223
230
  ngOnInit() { }
224
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TextWriterBuiderComponent, deps: [{ token: i1.UntypedFormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
225
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TextWriterBuiderComponent, isStandalone: true, selector: "app-text-writer-buider", usesInheritance: true, ngImport: i0, template: "<div>\r\n <div>\r\n <h5>Constructor de formulario con texto</h5>\r\n </div>\r\n\r\n <div>\r\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\r\n <input pInputText type=\"text\" nbInput fullWidth formControlName=\"response\" placeholder=\"Respuesta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"\" nbInput fullWidth formControlName=\"hint\"\r\n placeholder=\"Escribe una pista para esta pregunta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"text\" nbInput fullWidth formControlName=\"explanation\"\r\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\r\n </form>\r\n </div>\r\n\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\"\r\n [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\"\r\n severity=\"secondary\"></p-button>\r\n </div>\r\n</div>", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: "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: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
231
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextWriterBuiderComponent, deps: [{ token: i1.UntypedFormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
232
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TextWriterBuiderComponent, isStandalone: true, selector: "app-text-writer-buider", usesInheritance: true, ngImport: i0, template: "<div>\r\n <div>\r\n <h5>Constructor de formulario con texto</h5>\r\n </div>\r\n\r\n <div>\r\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\r\n <input pInputText type=\"text\" nbInput fullWidth formControlName=\"response\" placeholder=\"Respuesta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"\" nbInput fullWidth formControlName=\"hint\"\r\n placeholder=\"Escribe una pista para esta pregunta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"text\" nbInput fullWidth formControlName=\"explanation\"\r\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\r\n </form>\r\n </div>\r\n\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\"\r\n [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\"\r\n severity=\"secondary\"></p-button>\r\n </div>\r\n</div>", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: "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: 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
226
233
  }
227
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TextWriterBuiderComponent, decorators: [{
234
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextWriterBuiderComponent, decorators: [{
228
235
  type: Component,
229
236
  args: [{ selector: 'app-text-writer-buider', standalone: true, imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule], template: "<div>\r\n <div>\r\n <h5>Constructor de formulario con texto</h5>\r\n </div>\r\n\r\n <div>\r\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\r\n <input pInputText type=\"text\" nbInput fullWidth formControlName=\"response\" placeholder=\"Respuesta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"\" nbInput fullWidth formControlName=\"hint\"\r\n placeholder=\"Escribe una pista para esta pregunta\" />\r\n\r\n <input pInputText class=\"form-input\" type=\"text\" nbInput fullWidth formControlName=\"explanation\"\r\n placeholder=\"Excribe una explicaci\u00F3n para la respuesta\" />\r\n </form>\r\n </div>\r\n\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\"\r\n [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\"\r\n severity=\"secondary\"></p-button>\r\n </div>\r\n</div>", styles: ["nb-card{width:60vw}.builder-form{padding:5px}.form-input{margin-top:10px}.mar-top{margin:5px}\n"] }]
230
237
  }], ctorParameters: () => [{ type: i1.UntypedFormBuilder }, { type: i2.DynamicDialogRef }] });
@@ -264,10 +271,10 @@ class TextWriterComponent extends ComponentWithForm {
264
271
  }
265
272
  return true;
266
273
  }
267
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TextWriterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
268
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TextWriterComponent, isStandalone: true, selector: "app-text-writer", inputs: { config: "config" }, usesInheritance: true, ngImport: i0, template: "<input [class]=\"this.status\" pInputText [formControl]=\"control\" fieldSize=\"small\" type=\"text\" placeholder=\"Respuesta\"\n [size]=\"size\" />\n\n<!-- [ngClass]=\"{ 'selected-radio': 'true']}\" -->", styles: [".warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"], dependencies: [{ 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: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
274
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextWriterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
275
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TextWriterComponent, isStandalone: true, selector: "app-text-writer", inputs: { config: "config" }, usesInheritance: true, ngImport: i0, template: "<input [class]=\"this.status\" pInputText [formControl]=\"control\" fieldSize=\"small\" type=\"text\" placeholder=\"Respuesta\"\n [size]=\"size\" />\n\n<!-- [ngClass]=\"{ 'selected-radio': 'true']}\" -->", styles: [".warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"], dependencies: [{ 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: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }] }); }
269
276
  }
270
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TextWriterComponent, decorators: [{
277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextWriterComponent, decorators: [{
271
278
  type: Component,
272
279
  args: [{ selector: 'app-text-writer', standalone: true, imports: [FormsModule, ReactiveFormsModule, InputTextModule], template: "<input [class]=\"this.status\" pInputText [formControl]=\"control\" fieldSize=\"small\" type=\"text\" placeholder=\"Respuesta\"\n [size]=\"size\" />\n\n<!-- [ngClass]=\"{ 'selected-radio': 'true']}\" -->", styles: [".warning{border-color:#f0ad4e}.danger{border-color:#e1211b}\n"] }]
273
280
  }], ctorParameters: () => [], propDecorators: { config: [{
@@ -284,12 +291,12 @@ class TranslationSwitcherBuilderComponent extends ComponentBuilder {
284
291
  }
285
292
  // Este componente reutiliza completamente el form del padre y todos los métodos
286
293
  ngOnInit() { }
287
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslationSwitcherBuilderComponent, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
288
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TranslationSwitcherBuilderComponent, isStandalone: true, selector: "app-translation-switcher-builder", usesInheritance: true, ngImport: i0, template: "<div>\n <div>\n <h5>Constructor de translation switcher</h5>\n </div>\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input style=\"width: 100%\" pInputText type=\"text\" nbInput fullWidth formControlName=\"text\" placeholder=\"Texto para visualizar\" />\n\n <br /><br />\n\n <input\n style=\"width: 100%\"\n pInputText\n class=\"form-input\"\n type=\"\"\n nbInput\n fullWidth\n formControlName=\"response\"\n placeholder=\"Traducci\u00F3n al hacer clic\" />\n </form>\n </div>\n <br />\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: "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: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
294
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslationSwitcherBuilderComponent, deps: [{ token: i1.FormBuilder }, { token: i2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
295
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TranslationSwitcherBuilderComponent, isStandalone: true, selector: "app-translation-switcher-builder", usesInheritance: true, ngImport: i0, template: "<div>\n <div>\n <h5>Constructor de translation switcher</h5>\n </div>\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input style=\"width: 100%\" pInputText type=\"text\" nbInput fullWidth formControlName=\"text\" placeholder=\"Texto para visualizar\" />\n\n <br /><br />\n\n <input\n style=\"width: 100%\"\n pInputText\n class=\"form-input\"\n type=\"\"\n nbInput\n fullWidth\n formControlName=\"response\"\n placeholder=\"Traducci\u00F3n al hacer clic\" />\n </form>\n </div>\n <br />\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n</div>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: "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: 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
289
296
  }
290
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslationSwitcherBuilderComponent, decorators: [{
297
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslationSwitcherBuilderComponent, decorators: [{
291
298
  type: Component,
292
- args: [{ selector: 'app-translation-switcher-builder', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div>\n <div>\n <h5>Constructor de translation switcher</h5>\n </div>\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input style=\"width: 100%\" pInputText type=\"text\" nbInput fullWidth formControlName=\"text\" placeholder=\"Texto para visualizar\" />\n\n <br /><br />\n\n <input\n style=\"width: 100%\"\n pInputText\n class=\"form-input\"\n type=\"\"\n nbInput\n fullWidth\n formControlName=\"response\"\n placeholder=\"Traducci\u00F3n al hacer clic\" />\n </form>\n </div>\n <br />\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n</div>\n", styles: [":host{display:block}\n"] }]
299
+ args: [{ selector: 'app-translation-switcher-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div>\n <div>\n <h5>Constructor de translation switcher</h5>\n </div>\n\n <div>\n <form class=\"builder-form\" [formGroup]=\"formGroup\">\n <input style=\"width: 100%\" pInputText type=\"text\" nbInput fullWidth formControlName=\"text\" placeholder=\"Texto para visualizar\" />\n\n <br /><br />\n\n <input\n style=\"width: 100%\"\n pInputText\n class=\"form-input\"\n type=\"\"\n nbInput\n fullWidth\n formControlName=\"response\"\n placeholder=\"Traducci\u00F3n al hacer clic\" />\n </form>\n </div>\n <br />\n <div>\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\n <p-button (click)=\"showCode()\" [disabled]=\"formGroup.invalid\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\n </div>\n</div>\n", styles: [":host{display:block}\n"] }]
293
300
  }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.DynamicDialogRef }] });
294
301
 
295
302
  class TranslationSwitcherComponent {
@@ -308,16 +315,77 @@ class TranslationSwitcherComponent {
308
315
  this.visibleText = this.config.settings.text;
309
316
  }
310
317
  }
311
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslationSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
312
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TranslationSwitcherComponent, isStandalone: true, selector: "app-translation-switcher", inputs: { config: "config" }, ngImport: i0, template: "<button\n pButton\n style=\"padding: 0px 2px\"\n severity=\"help\"\n size=\"small\"\n (click)=\"switchTranslation()\"\n [label]=\"visibleText\"\n [text]=\"true\"\n [rounded]=\"true\"></button>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
318
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslationSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
319
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: TranslationSwitcherComponent, isStandalone: true, selector: "app-translation-switcher", inputs: { config: "config" }, ngImport: i0, template: "<button\n pButton\n style=\"padding: 0px 2px\"\n severity=\"help\"\n size=\"small\"\n (click)=\"switchTranslation()\"\n [label]=\"visibleText\"\n [text]=\"true\"\n [rounded]=\"true\"></button>\n", styles: [":host{display:block}\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"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
313
320
  }
314
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslationSwitcherComponent, decorators: [{
321
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslationSwitcherComponent, decorators: [{
315
322
  type: Component,
316
- args: [{ selector: 'app-translation-switcher', standalone: true, imports: [CommonModule, ButtonModule], changeDetection: ChangeDetectionStrategy.Default, template: "<button\n pButton\n style=\"padding: 0px 2px\"\n severity=\"help\"\n size=\"small\"\n (click)=\"switchTranslation()\"\n [label]=\"visibleText\"\n [text]=\"true\"\n [rounded]=\"true\"></button>\n", styles: [":host{display:block}\n"] }]
323
+ args: [{ selector: 'app-translation-switcher', standalone: true, imports: [ButtonModule], changeDetection: ChangeDetectionStrategy.Default, template: "<button\n pButton\n style=\"padding: 0px 2px\"\n severity=\"help\"\n size=\"small\"\n (click)=\"switchTranslation()\"\n [label]=\"visibleText\"\n [text]=\"true\"\n [rounded]=\"true\"></button>\n", styles: [":host{display:block}\n"] }]
317
324
  }], propDecorators: { config: [{
318
325
  type: Input
319
326
  }] } });
320
327
 
328
+ class SpeakerBuilderComponent extends ComponentBuilder {
329
+ constructor() {
330
+ super(...arguments);
331
+ this.tts = signal(undefined);
332
+ }
333
+ handleTtsGenerated(event) {
334
+ console.log('TTS generated:', event);
335
+ this.tts.set(event);
336
+ }
337
+ // Update return type and add settings object
338
+ getCode() {
339
+ const ttsValue = this.tts();
340
+ if (!ttsValue) {
341
+ // Handle the case where tts is undefined, maybe throw an error or return a default config
342
+ console.error('TTS data is not generated yet.');
343
+ // Depending on requirements, you might return a default/empty config or throw
344
+ return { component: this.questionType, inputs: {} }; // Example: return empty inputs
345
+ }
346
+ const code = {
347
+ component: this.questionType,
348
+ inputs: { tts: ttsValue },
349
+ };
350
+ return code;
351
+ }
352
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
353
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: SpeakerBuilderComponent, isStandalone: true, selector: "app-speaker-builder", usesInheritance: true, ngImport: i0, template: "<div>\r\n <div>\r\n <h5>Constructor de Speaker</h5>\r\n </div>\r\n\r\n <lib-ngx-tts (ttsGenerated)=\"handleTtsGenerated($event)\"></lib-ngx-tts>\r\n\r\n <br />\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"!tts()\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\r\n </div>\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: InputTextModule }, { 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: NgxTtsComponent, selector: "lib-ngx-tts", inputs: ["path"], outputs: ["ttsGenerated"] }] }); }
354
+ }
355
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerBuilderComponent, decorators: [{
356
+ type: Component,
357
+ args: [{ selector: 'app-speaker-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, InputTextModule, ButtonModule, DropdownModule, NgxTtsComponent], template: "<div>\r\n <div>\r\n <h5>Constructor de Speaker</h5>\r\n </div>\r\n\r\n <lib-ngx-tts (ttsGenerated)=\"handleTtsGenerated($event)\"></lib-ngx-tts>\r\n\r\n <br />\r\n <div>\r\n <p-button (click)=\"copyToClipboard()\" [disabled]=\"formGroup.invalid\" label=\"Copia C\u00F3digo\" [rounded]=\"true\"></p-button>\r\n <p-button (click)=\"showCode()\" [disabled]=\"!tts()\" label=\"Mostrar\" [rounded]=\"true\" severity=\"secondary\"></p-button>\r\n </div>\r\n</div>\r\n" }]
358
+ }] });
359
+
360
+ // ❌ can use this until i copy services to this lib
361
+ // import { CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
362
+ class SpeakerComponent {
363
+ ngOnInit() {
364
+ // throw new Error('Method not implemented.');
365
+ }
366
+ // voiceOptions = VoiceOptions;
367
+ // constructor(private speachService: SpeechService, private audioService: AudioService) {}
368
+ // ngOnInit(): void {}
369
+ speach() {
370
+ console.log('should speech but will do in next version');
371
+ // if (this.config.audio) {
372
+ // this.audioService.playAudio(this.config.audio.url);
373
+ // } else {
374
+ // this.speachService.speach(this.config.settings.text);
375
+ // }
376
+ }
377
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
378
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: SpeakerComponent, isStandalone: true, selector: "app-speaker", inputs: { config: "config", tts: "tts" }, ngImport: i0, template: "<button\r\n pButton\r\n style=\"padding: 0px 2px\"\r\n severity=\"help\"\r\n size=\"small\"\r\n (click)=\"speach()\"\r\n [label]=\"config?.settings?.text\"\r\n [text]=\"true\"\r\n [rounded]=\"true\"></button>\r\n", styles: [".lisen{cursor:pointer}\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"] }] }); }
379
+ }
380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerComponent, decorators: [{
381
+ type: Component,
382
+ args: [{ selector: 'app-speaker', standalone: true, imports: [ButtonModule], template: "<button\r\n pButton\r\n style=\"padding: 0px 2px\"\r\n severity=\"help\"\r\n size=\"small\"\r\n (click)=\"speach()\"\r\n [label]=\"config?.settings?.text\"\r\n [text]=\"true\"\r\n [rounded]=\"true\"></button>\r\n", styles: [".lisen{cursor:pointer}\n"] }]
383
+ }], propDecorators: { config: [{
384
+ type: Input
385
+ }], tts: [{
386
+ type: Input
387
+ }] } });
388
+
321
389
  var LessonComponentEnum;
322
390
  (function (LessonComponentEnum) {
323
391
  LessonComponentEnum["Selector"] = "selector";
@@ -329,7 +397,7 @@ var LessonComponentEnum;
329
397
  })(LessonComponentEnum || (LessonComponentEnum = {}));
330
398
  const LessonComponentBuilders = {
331
399
  [LessonComponentEnum.Selector]: SelectorBuilderComponent,
332
- // [LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
400
+ [LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
333
401
  [LessonComponentEnum.TextWriter]: TextWriterBuiderComponent,
334
402
  // [LessonComponentEnum.VerbSummary]: VerbSummaryBuilderComponent,
335
403
  // [LessonComponentEnum.WordSummary]: WordSummaryBuilderComponent,
@@ -337,7 +405,7 @@ const LessonComponentBuilders = {
337
405
  };
338
406
  const LessonComponents = {
339
407
  [LessonComponentEnum.Selector]: SelectorComponent,
340
- // [LessonComponentEnum.Speaker]: SpeakerComponent,
408
+ [LessonComponentEnum.Speaker]: SpeakerComponent,
341
409
  [LessonComponentEnum.TextWriter]: TextWriterComponent,
342
410
  // [LessonComponentEnum.VerbSummary]: VerbSummaryComponent,
343
411
  // [LessonComponentEnum.WordSummary]: WordSummaryComponent,
@@ -362,6 +430,16 @@ function provideLessonsService(serviceImplementation) {
362
430
  },
363
431
  ];
364
432
  }
433
+ // export type LessonComponentsType = 'selector' | 'speaker' | 'text-writer' | 'verb-summary' | 'word-summary';
434
+ // export function getLessonComponentClass(type: LessonComponentsType) {
435
+ // // return LessonComponents[type];
436
+ // return null;
437
+ // }
438
+ // Removed duplicate LessonComponentConfiguration definition
439
+ // Removed duplicate StorageFile definition
440
+ // Removed duplicate AudioStorage definition
441
+ // Removed duplicate SpeakerCompConfiguration definition
442
+ // Removed duplicate LessonComponentInterface definition
365
443
  // export const LessonComponents = {
366
444
  // [LessonComponentEnum.Selector]: 1,
367
445
  // [LessonComponentEnum.Speaker]: 2,
@@ -394,10 +472,10 @@ class LangDescTranslationPipe {
394
472
  return LangCodeDescription[value];
395
473
  }
396
474
  }
397
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: LangDescTranslationPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
398
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: LangDescTranslationPipe, isStandalone: true, name: "langDesc" }); }
475
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
476
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, isStandalone: true, name: "langDesc" }); }
399
477
  }
400
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: LangDescTranslationPipe, decorators: [{
478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, decorators: [{
401
479
  type: Pipe,
402
480
  args: [{
403
481
  name: 'langDesc',
@@ -425,10 +503,10 @@ class FlagLanguagePipe {
425
503
  return '';
426
504
  }
427
505
  }
428
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: FlagLanguagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
429
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: FlagLanguagePipe, isStandalone: true, name: "flagEmoji" }); }
506
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FlagLanguagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
507
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: FlagLanguagePipe, isStandalone: true, name: "flagEmoji" }); }
430
508
  }
431
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: FlagLanguagePipe, decorators: [{
509
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: FlagLanguagePipe, decorators: [{
432
510
  type: Pipe,
433
511
  args: [{
434
512
  name: 'flagEmoji',
@@ -460,18 +538,14 @@ var NotionExportType;
460
538
  var EventCard;
461
539
  (function (EventCard) {
462
540
  EventCard["Edit"] = "edit";
463
- EventCard["Remove"] = "remove";
464
- EventCard["Take"] = "take";
541
+ EventCard["Delete"] = "delete";
542
+ EventCard["Select"] = "select";
465
543
  EventCard["Qr"] = "qr";
466
544
  })(EventCard || (EventCard = {}));
467
545
  class DcLessonCardComponent {
468
546
  constructor() {
469
- this.isAdmin = true;
470
- // TODO: i think i can refactor this to create actions, so only one event emitting actions.
471
- this.take = new EventEmitter();
472
- this.edit = new EventEmitter();
473
- this.remove = new EventEmitter();
474
- this.qr = new EventEmitter();
547
+ this.showOptions = true;
548
+ this.onAction = new EventEmitter();
475
549
  this.coverUrl = 'assets/background/default-background.webp';
476
550
  this.eventType = EventCard;
477
551
  this.items = [
@@ -486,14 +560,14 @@ class DcLessonCardComponent {
486
560
  label: 'Eliminar',
487
561
  icon: 'pi pi-trash',
488
562
  command: () => {
489
- this.eventCard(EventCard.Remove);
563
+ this.eventCard(EventCard.Delete);
490
564
  },
491
565
  },
492
566
  {
493
567
  label: 'Tomar lección',
494
568
  icon: 'pi pi-play',
495
569
  command: () => {
496
- this.eventCard(EventCard.Take);
570
+ this.eventCard(EventCard.Select);
497
571
  },
498
572
  },
499
573
  ];
@@ -506,36 +580,30 @@ class DcLessonCardComponent {
506
580
  eventCard(eventType) {
507
581
  switch (eventType) {
508
582
  case EventCard.Edit:
509
- this.edit.emit(this.lesson);
583
+ this.onAction.emit({ action: 'edit', item: this.lesson });
510
584
  break;
511
- case EventCard.Remove:
512
- this.remove.emit(this.lesson);
585
+ case EventCard.Delete:
586
+ this.onAction.emit({ action: 'delete', item: this.lesson });
513
587
  break;
514
- case EventCard.Take:
515
- this.take.emit(this.lesson);
588
+ case EventCard.Select:
589
+ this.onAction.emit({ action: 'select', item: this.lesson });
516
590
  break;
517
591
  case EventCard.Qr:
518
- this.qr.emit(this.lesson);
592
+ this.onAction.emit({ action: 'qr', item: this.lesson });
519
593
  break;
520
594
  }
521
595
  }
522
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
523
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", isAdmin: "isAdmin" }, outputs: { take: "take", edit: "edit", remove: "remove", qr: "qr" }, ngImport: i0, template: "<div class=\"card-container\">\r\n <p-speeddial\r\n class=\"dial-button\"\r\n [model]=\"items\"\r\n [radius]=\"70\"\r\n type=\"quarter-circle\"\r\n direction=\"down-left\"\r\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\r\n\r\n <p-card>\r\n <div class=\"lesson-card\">\r\n <div class=\"photo\">\r\n <img [src]=\"coverUrl\" alt=\"\" />\r\n </div>\r\n\r\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\r\n\r\n <div class=\"description\">\r\n <h1>{{ lesson.title }}</h1>\r\n <p>{{ lesson.description }}</p>\r\n <div class=\"card-footer\">\r\n <div class=\"status-tags\">\r\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\r\n <span *ngIf=\"lesson.taken?.status == 'passed'\" class=\"status-tag success\">Tomada</span>\r\n <span *ngIf=\"lesson.taken?.status == 'failed'\" class=\"status-tag danger\">Fallida</span>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Take)\" severity=\"primary\"> </p-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </p-card>\r\n</div>\r\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}.description{position:relative;z-index:2;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0006,#000c);height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem}.description p{margin-bottom:1rem;flex-grow:1}.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}.actions ::ng-deep .p-button{font-size:.9rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$2.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"] }] }); }
596
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
597
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions" }, 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 }\n <p-card>\n <div class=\"lesson-card\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title }}</h1>\n <p>{{ lesson.description }}</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 <span class=\"status-tag success\">Tomada</span>\n }\n @if (lesson.taken?.status == 'failed') {\n <span class=\"status-tag danger\">Fallida</span>\n }\n </div>\n\n <div class=\"actions\">\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}.description p{margin-bottom:1rem;flex-grow:1}.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}.actions ::ng-deep .p-button{font-size:.9rem}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$2.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.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }] }); }
524
598
  }
525
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcLessonCardComponent, decorators: [{
599
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, decorators: [{
526
600
  type: Component,
527
- args: [{ selector: 'dc-lesson-card', standalone: true, imports: [NgFor, NgIf, DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule], template: "<div class=\"card-container\">\r\n <p-speeddial\r\n class=\"dial-button\"\r\n [model]=\"items\"\r\n [radius]=\"70\"\r\n type=\"quarter-circle\"\r\n direction=\"down-left\"\r\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\r\n\r\n <p-card>\r\n <div class=\"lesson-card\">\r\n <div class=\"photo\">\r\n <img [src]=\"coverUrl\" alt=\"\" />\r\n </div>\r\n\r\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\r\n\r\n <div class=\"description\">\r\n <h1>{{ lesson.title }}</h1>\r\n <p>{{ lesson.description }}</p>\r\n <div class=\"card-footer\">\r\n <div class=\"status-tags\">\r\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\r\n <span *ngIf=\"lesson.taken?.status == 'passed'\" class=\"status-tag success\">Tomada</span>\r\n <span *ngIf=\"lesson.taken?.status == 'failed'\" class=\"status-tag danger\">Fallida</span>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Take)\" severity=\"primary\"> </p-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </p-card>\r\n</div>\r\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}.description{position:relative;z-index:2;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0006,#000c);height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem}.description p{margin-bottom:1rem;flex-grow:1}.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}.actions ::ng-deep .p-button{font-size:.9rem}\n"] }]
601
+ args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule], 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 }\n <p-card>\n <div class=\"lesson-card\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title }}</h1>\n <p>{{ lesson.description }}</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 <span class=\"status-tag success\">Tomada</span>\n }\n @if (lesson.taken?.status == 'failed') {\n <span class=\"status-tag danger\">Fallida</span>\n }\n </div>\n\n <div class=\"actions\">\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}.description p{margin-bottom:1rem;flex-grow:1}.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}.actions ::ng-deep .p-button{font-size:.9rem}\n"] }]
528
602
  }], propDecorators: { lesson: [{
529
603
  type: Input
530
- }], isAdmin: [{
604
+ }], showOptions: [{
531
605
  type: Input
532
- }], take: [{
533
- type: Output
534
- }], edit: [{
535
- type: Output
536
- }], remove: [{
537
- type: Output
538
- }], qr: [{
606
+ }], onAction: [{
539
607
  type: Output
540
608
  }] } });
541
609
 
@@ -559,15 +627,10 @@ class DCLessonListComponent extends PaginationBase {
559
627
  this.route = route;
560
628
  this.lessonsService = lessonsService;
561
629
  this.toastrService = toastrService;
562
- this.isAdmin = false;
630
+ this.showOptions = true;
563
631
  this.customFilters = [];
564
- this.viewType = 'card';
565
- this.actions = TableViewActions;
566
- this.onTakeLesson = new EventEmitter();
567
- this.onEditLesson = new EventEmitter();
568
- this.onRemoveLesson = new EventEmitter();
569
- this.qr = new EventEmitter();
570
- this.onNewLesson = new EventEmitter();
632
+ this.viewType = 'cards';
633
+ // readonly actions = input<MenuItem[]>(TableViewActions);
571
634
  this.columns = tableViewColumns;
572
635
  this.cardComponent = null;
573
636
  this.cardEventSubs = [];
@@ -586,14 +649,8 @@ class DCLessonListComponent extends PaginationBase {
586
649
  subscribeToCardEvents() {
587
650
  this.outlets.forEach((outlet) => {
588
651
  const instance = outlet.componentInstance;
589
- this.cardEventSubs.push(instance.take.subscribe((lesson) => {
590
- this.takeLesson(lesson);
591
- }), instance.edit.subscribe((lesson) => {
592
- this.editLesson(lesson);
593
- }), instance.remove.subscribe((lesson) => {
594
- this.removeLesson(lesson);
595
- }), instance.qr.subscribe((lesson) => {
596
- this.generateQR(lesson);
652
+ this.cardEventSubs.push(instance.onAction.subscribe((lesson) => {
653
+ this.doOrEmitAction(lesson);
597
654
  }));
598
655
  });
599
656
  }
@@ -622,23 +679,10 @@ class DCLessonListComponent extends PaginationBase {
622
679
  this.cdr.detectChanges();
623
680
  }
624
681
  }
625
- goToLessonDetails(id) {
626
- // this.router.navigate([`${RouteNames.App}/${RouteNames.LessonsV2}/details/${id}`]);
627
- alert('TODO: goToLessonDetails');
628
- }
629
682
  search(searchText) {
630
683
  this.filterConfig['text'] = searchText;
631
684
  this.getPaginatedLessons(this.filterConfig);
632
685
  }
633
- generateQR(lesson) {
634
- this.qr.emit(lesson);
635
- }
636
- takeLesson(lesson) {
637
- this.onTakeLesson.emit(lesson);
638
- }
639
- editLesson(lesson) {
640
- this.onEditLesson.emit(lesson);
641
- }
642
686
  removeLesson(lesson) {
643
687
  const response = confirm('¿Estás seguro de querer eliminar esta lección?');
644
688
  if (response) {
@@ -649,30 +693,44 @@ class DCLessonListComponent extends PaginationBase {
649
693
  }
650
694
  }
651
695
  newLesson() {
652
- this.onNewLesson.emit();
696
+ this.onAction.emit({ action: 'new' });
653
697
  }
654
- filterChanged(filters) {
655
- console.log('filterChanged in dc-lesson-list', filters);
656
- this.filterConfig = filters;
657
- this.filterConfig.returnProps = returnProperties;
658
- this.getPaginatedLessons(this.filterConfig);
698
+ applyFilterBarEvent(filterEvent) {
699
+ if (filterEvent.action == 'changeView') {
700
+ this.viewType = this.viewType === 'table' ? 'cards' : 'table';
701
+ return;
702
+ }
703
+ else {
704
+ console.log('filterChanged in dc-lesson-list', filterEvent);
705
+ this.filterConfig = filterEvent.item;
706
+ this.filterConfig.returnProps = returnProperties;
707
+ this.getPaginatedLessons(this.filterConfig);
708
+ }
659
709
  }
660
710
  loadData() {
661
711
  return this.getPaginatedLessons(this.filterConfig);
662
712
  }
663
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.Router }, { token: i1$1.ActivatedRoute }, { token: LESSONS_TOKEN }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
664
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { isAdmin: "isAdmin", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType", actions: "actions" }, outputs: { onTakeLesson: "onTakeLesson", onEditLesson: "onEditLesson", onRemoveLesson: "onRemoveLesson", qr: "qr", onNewLesson: "onNewLesson" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onSearch)=\"search($event)\" (onFilterChange)=\"filterChanged($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" [actions]=\"actions\" (onAction)=\"doAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n isAdmin: isAdmin\n }\">\n </ng-container>\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 [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onAction", "onChangeSort", "onNew", "onSearch", "onFilterChange"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i3$2.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
713
+ doOrEmitAction(actionEvent) {
714
+ if (actionEvent.action === 'delete') {
715
+ this.removeLesson(actionEvent.item);
716
+ }
717
+ else {
718
+ this.onAction.emit(actionEvent);
719
+ }
720
+ }
721
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.Router }, { token: i1$2.ActivatedRoute }, { token: LESSONS_TOKEN }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
722
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { showOptions: "showOptions", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\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 [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "items", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
665
723
  }
666
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonListComponent, decorators: [{
724
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, decorators: [{
667
725
  type: Component,
668
- args: [{ selector: 'dc-lesson-list', standalone: true, imports: [CommonModule, RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onSearch)=\"search($event)\" (onFilterChange)=\"filterChanged($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" [actions]=\"actions\" (onAction)=\"doAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n isAdmin: isAdmin\n }\">\n </ng-container>\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 [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"] }]
669
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.Router }, { type: i1$1.ActivatedRoute }, { type: LessonsAbstractService, decorators: [{
726
+ args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\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 [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"] }]
727
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.Router }, { type: i1$2.ActivatedRoute }, { type: LessonsAbstractService, decorators: [{
670
728
  type: Inject,
671
729
  args: [LESSONS_TOKEN]
672
- }] }, { type: i5$1.ToastAlertsAbstractService, decorators: [{
730
+ }] }, { type: i4$1.ToastAlertsAbstractService, decorators: [{
673
731
  type: Inject,
674
732
  args: [TOAST_ALERTS_TOKEN]
675
- }] }], propDecorators: { isAdmin: [{
733
+ }] }], propDecorators: { showOptions: [{
676
734
  type: Input
677
735
  }], customCardComponent: [{
678
736
  type: Input
@@ -680,34 +738,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
680
738
  type: Input
681
739
  }], viewType: [{
682
740
  type: Input
683
- }], actions: [{
684
- type: Input
685
- }], onTakeLesson: [{
686
- type: Output
687
- }], onEditLesson: [{
688
- type: Output
689
- }], onRemoveLesson: [{
690
- type: Output
691
- }], qr: [{
692
- type: Output
693
- }], onNewLesson: [{
694
- type: Output
695
741
  }], outlets: [{
696
742
  type: ViewChildren,
697
743
  args: ['outlet']
698
744
  }] } });
699
745
 
700
746
  class DCLessonFormComponent {
701
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
702
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DCLessonFormComponent, isStandalone: true, selector: "dc-lesson-form", ngImport: i0, 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"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
747
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
748
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: DCLessonFormComponent, isStandalone: true, selector: "dc-lesson-form", ngImport: i0, 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"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }] }); }
703
749
  }
704
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonFormComponent, decorators: [{
750
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonFormComponent, decorators: [{
705
751
  type: Component,
706
- args: [{ selector: 'dc-lesson-form', standalone: true, imports: [CommonModule, 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"] }]
752
+ 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"] }]
707
753
  }] });
708
754
 
709
- // TODO: i need strategy to create prompt dinamically dpending on user language and level.
710
- const DefaultLessonAgentCard = {
755
+ // Re-define or import constants if they are not exported from the component file
756
+ const DEFAULT_LESSON_AGENT_CARD = {
711
757
  conversationSettings: {
712
758
  conversationType: ConversationType.General,
713
759
  textEngine: TextEngines.SimpleText,
@@ -723,105 +769,270 @@ const DefaultLessonAgentCard = {
723
769
  },
724
770
  model: { provider: 'google' },
725
771
  };
772
+ function getDefaultLessonEvaluatorAgentCard(lessonText) {
773
+ return {
774
+ expectedResponseType: `interface EvalResult {
775
+ score: number; // Score of the user's response 0 to 3
776
+ feedback: string; // Feedback of the user's understanding of the conversation
777
+ }`,
778
+ messages: [],
779
+ model: { id: 'gpt-4o-mini', provider: 'openai' },
780
+ sources: [lessonText],
781
+ task: `User is reading a taking a lesson, now their are having a conversation,
782
+ you have to evaluate the current conversation, and give a feedback of the user understanding of the lesson,
783
+ this is the lesson: ${lessonText}`,
784
+ };
785
+ }
786
+ class LessonAIService {
787
+ // TODO: Inject the application-level UserService
788
+ // private readonly userService = inject(UserService);
789
+ constructor() {
790
+ this.lessonService = inject(LESSONS_TOKEN);
791
+ }
792
+ /**
793
+ * Generates the necessary agent cards for a lesson chat session.
794
+ * @param lesson The lesson data.
795
+ * @returns An object containing the master agent card and the evaluator agent card.
796
+ */
797
+ async generateAgentCards(lesson) {
798
+ // TODO: Implement the logic moved from DCLessonRendererComponent.startAI here
799
+ // 1. Get user data using the injected UserService
800
+ // 2. Extract lesson text using lessonService
801
+ // 3. Build prompts (scenario, userInformationPrompt)
802
+ // 4. Configure and return the agent cards
803
+ alert('AI User data fetching needs refactoring into this service.');
804
+ // Placeholder for user data - replace with actual service call
805
+ const user = {
806
+ personalData: { firstname: 'Test', lastname: 'User' },
807
+ settings: { targetLanguage: 'en', baseLanguage: 'es' },
808
+ languageProgress: { en: { level: '1' } },
809
+ }; // Replace 'any' with your actual User type/interface
810
+ if (!user) {
811
+ console.error('User data not available to generate agent cards.');
812
+ // Handle error appropriately - maybe return null or throw?
813
+ return null;
814
+ }
815
+ const lessonText = this.lessonService.extractTextFromHtml(lesson.textCoded);
816
+ const scenario = `The user is reading lessons through this app interface. They will now talk with you, and you need to evaluate their understanding of the lesson.
817
+ Ask friendly questions throughout the conversation and help them learn English. Here is the lesson text the user just read:
818
+ ${lessonText}
819
+ In your next reply, start by greeting the user, asking something about the lesson, and then continue the conversation.`;
820
+ const targetLevel = parseInt(user.languageProgress[user.settings.targetLanguage]?.level ?? '1');
821
+ const langTargetDesc = LangCodeDescription[user.settings.targetLanguage] ?? user.settings.targetLanguage;
822
+ const langBaseDesc = LangCodeDescription[user.settings.baseLanguage] ?? user.settings.baseLanguage;
823
+ let userInformationPrompt = `
824
+ User information: user name is ${user.personalData.firstname} ${user.personalData.lastname}, their native language is ${langBaseDesc},
825
+ and right now is learning ${langTargetDesc}, their current level is ${targetLevel} out of 5.`;
826
+ if (targetLevel <= 2) {
827
+ userInformationPrompt += `\nUser is a beginner in ${langTargetDesc}, always reply mainly in ${langBaseDesc}, but during the conversation use simple words and phrases in ${langTargetDesc} to help them learn.`;
828
+ }
829
+ // Create a deep copy of the default card to avoid modifying the constant
830
+ const masterAgent = JSON.parse(JSON.stringify(DEFAULT_LESSON_AGENT_CARD));
831
+ masterAgent.characterCard.data.scenario = scenario;
832
+ masterAgent.characterCard.data.post_history_instructions += `\n${userInformationPrompt}`;
833
+ const evaluatorAgent = getDefaultLessonEvaluatorAgentCard(lessonText);
834
+ return { masterAgent, evaluatorAgent };
835
+ }
836
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonAIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
837
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonAIService, providedIn: 'root' }); }
838
+ }
839
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonAIService, decorators: [{
840
+ type: Injectable,
841
+ args: [{
842
+ providedIn: 'root', // Or provide appropriately if it's library-specific
843
+ }]
844
+ }], ctorParameters: () => [] });
845
+
726
846
  class DCLessonRendererComponent {
727
- constructor(renderer, viewContainerRef, activatedRoute, toastrService, lessonService, agentCardsService, conversationBuilder) {
728
- this.renderer = renderer;
729
- this.viewContainerRef = viewContainerRef;
730
- this.activatedRoute = activatedRoute;
731
- this.toastrService = toastrService;
732
- this.lessonService = lessonService;
733
- this.agentCardsService = agentCardsService;
734
- this.conversationBuilder = conversationBuilder;
735
- this.lessonId = this.activatedRoute.snapshot.paramMap.get('id');
736
- this.dataText = `<h1>Nueva lección </h1> <p> Texto aquí</p>`;
847
+ constructor() {
848
+ // --- Signal Inputs ---
849
+ this.lessonInput = input(); // Input signal for lesson object
850
+ this.lessonIdInput = input(); // Input signal for lesson ID
851
+ this.test = input(false);
852
+ // --- Injected Services (using inject function) ---
853
+ this.renderer = inject(Renderer2);
854
+ this.viewContainerRef = inject(ViewContainerRef);
855
+ this.toastrService = inject(TOAST_ALERTS_TOKEN);
856
+ this.lessonService = inject(LESSONS_TOKEN);
857
+ this.lessonAIService = inject(LessonAIService); // Inject the new service
858
+ // --- State Signals ---
859
+ this.lesson = signal(undefined); // Internal lesson state signal
860
+ this.chatVisible = signal(false); // Signal for chat visibility
861
+ this.agentMasterLesson = signal(undefined); // Signal for agent card
862
+ this.evaluatorAgentCard = signal(undefined); // Signal for evaluator card
863
+ // --- Computed Signals ---
864
+ this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url); // Computed signal for imageCover
865
+ // --- Properties ---
737
866
  this.components = {};
738
- this.mainForm = new UntypedFormGroup({});
739
- this.chatVisible = false;
740
- }
741
- ngOnInit() {
742
- this.initLesson();
743
- }
744
- async initLesson() {
745
- await this.getLessonIfId();
746
- this.renderLesson();
867
+ this.mainForm = new FormGroup({});
868
+ // Effect to fetch lesson data if ID is provided and lesson object isn't
869
+ effect(async () => {
870
+ const lessonInput = this.lessonInput();
871
+ const lessonId = this.lessonIdInput();
872
+ if (lessonInput) {
873
+ this.lesson.set(lessonInput); // Use input lesson directly
874
+ }
875
+ else if (lessonId && !this.lesson()) {
876
+ // Fetch only if ID exists and internal lesson is not set
877
+ console.log(`Fetching lesson with ID: ${lessonId}`);
878
+ try {
879
+ // Consider adding a loading state signal here
880
+ const fetchedLesson = await this.lessonService.getLesson(lessonId);
881
+ this.lesson.set(fetchedLesson);
882
+ console.log('Fetched lesson:', fetchedLesson);
883
+ }
884
+ catch (error) {
885
+ console.error(`Failed to fetch lesson with ID: ${lessonId}`, error);
886
+ this.toastrService.error({ subtitle: 'Failed to load lesson data.', title: 'Error' });
887
+ this.lesson.set(undefined); // Reset lesson on error
888
+ }
889
+ finally {
890
+ // Reset loading state signal here
891
+ }
892
+ }
893
+ else if (!lessonInput && !lessonId) {
894
+ // Handle case where neither input is provided, maybe clear the lesson?
895
+ this.lesson.set(undefined);
896
+ }
897
+ }, { allowSignalWrites: true }); // Allow signal writes inside effect
898
+ // Effect to render the lesson whenever the lesson signal changes
899
+ effect(() => {
900
+ const currentLesson = this.lesson();
901
+ if (currentLesson) {
902
+ this._renderLesson(currentLesson);
903
+ }
904
+ else {
905
+ // Clear previous rendering if lesson becomes undefined
906
+ this._clearLessonRendering();
907
+ }
908
+ });
747
909
  }
748
- async getLessonIfId() {
749
- if (this.lesson) {
750
- return;
751
- }
752
- if (!this.lessonId) {
753
- return;
910
+ // --- Rendering Logic ---
911
+ _clearLessonRendering() {
912
+ // Destroy previously created dynamic components
913
+ Object.values(this.components).forEach((compRef) => compRef.destroy());
914
+ this.components = {};
915
+ // Clear the form
916
+ this.mainForm = new FormGroup({});
917
+ // Clear the HTML content
918
+ if (this.dynamicLesson?.nativeElement) {
919
+ this.dynamicLesson.nativeElement.innerHTML = '';
754
920
  }
755
- this.dataText = `<h1>Cargando </h1> <p>...</p>`;
756
- this.lesson = await this.lessonService.getLesson(this.lessonId);
757
- console.log('lesson', this.lesson);
758
- }
759
- renderLesson() {
760
- this.imageCover = this.lesson.media?.images?.find((img) => img.type === 'cover')?.url;
761
- console.log('imageCover', this.imageCover);
762
- // Pasos para renderizar,
763
- // 0) Usar la expresión regular para sustitur los componentes por un span con un id
764
- // 1) En el mismo proceso de remplazar se contruyen dinamicamente los componentes
765
- // 2) Agregar componentes al DOM en su respectivo spanid
766
- // TODO Optimización, es posible agregar el componente al DOM en el mismo paso en que se crea el span?
921
+ }
922
+ _renderLesson(lessonData) {
923
+ this._clearLessonRendering(); // Clear previous state first
924
+ console.log('Rendering lesson:', lessonData.id);
925
+ // console.log('Image cover URL:', this.imageCover()); // Access computed signal
926
+ // 1) Parse textCoded, create components, and build HTML structure
927
+ const { htmlContent, components } = this._parseAndCreateComponents(lessonData);
928
+ this.components = components;
929
+ // 2) Aggregate form controls from created components
930
+ this._aggregateFormControls(this.components);
931
+ // 3) Set the innerHTML of the target element
932
+ this.dynamicLesson.nativeElement.innerHTML = htmlContent;
933
+ // 4) Inject the component views into the DOM
934
+ this._injectComponentsIntoDom(this.components);
935
+ }
936
+ _parseAndCreateComponents(lessonData) {
767
937
  const r1 = new RegExp('~(.+?)~', 'g');
768
938
  let count = 0;
769
- this.components = {}; //Si se destruye la lección, se destruyen todos los componentes dinamicis? tODO necesito reiniciar?
770
- const lessonHtml = this.lesson.textCoded.replace(r1, (_matching, jsonCoded) => {
939
+ const createdComponents = {};
940
+ const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
771
941
  const componentName = `dynamicComp${count}`;
772
942
  count++;
773
- const componentRef = this.createComponentReferenceWithJson(jsonCoded);
943
+ const componentRef = this._createComponentReferenceWithJson(jsonCoded, lessonData);
774
944
  if (!componentRef) {
775
- return null;
945
+ console.error(`Failed to create component for: ${jsonCoded}`);
946
+ return '<!-- component creation failed -->'; // Placeholder in HTML
776
947
  }
777
- this.components[componentName] = componentRef;
778
- if (componentRef.instance.control) {
779
- // Algunos componentes tienen formularios, estos se copian en mainForm como un unico control
780
- this.mainForm.addControl(componentName, componentRef.instance.control);
781
- this.mainForm.get(componentName).setValidators([Validators.required]);
948
+ createdComponents[componentName] = componentRef;
949
+ return `<span id="${componentName}"></span>`; // Return span placeholder
950
+ });
951
+ return { htmlContent, components: createdComponents };
952
+ }
953
+ _aggregateFormControls(components) {
954
+ const newFormControls = {};
955
+ Object.entries(components).forEach(([name, componentRef]) => {
956
+ // Check if the instance has a control property that is a FormControl
957
+ if (componentRef.instance?.control instanceof FormControl) {
958
+ newFormControls[name] = componentRef.instance.control;
959
+ // Add required validator (consider making this configurable via component config?)
960
+ newFormControls[name].addValidators(Validators.required);
961
+ }
962
+ });
963
+ this.mainForm = new FormGroup(newFormControls); // Create the typed FormGroup
964
+ }
965
+ _injectComponentsIntoDom(components) {
966
+ Object.entries(components).forEach(([name, componentRef]) => {
967
+ const elementRef = document.getElementById(name); // Find the placeholder span
968
+ if (elementRef) {
969
+ this._addComponentToNode(componentRef, elementRef);
970
+ }
971
+ else {
972
+ console.warn(`Placeholder element with ID '${name}' not found in the DOM.`);
782
973
  }
783
- return `<span id="${componentName}"></span>`;
784
974
  });
785
- this.dynamicLesson.nativeElement.innerHTML = lessonHtml;
786
- for (let compName of Object.keys(this.components)) {
787
- // Interpolar componentes dentro del lugar correspondiente
788
- const elementRef = document.getElementById(compName);
789
- this.addComponentToNode(this.components[compName], elementRef);
790
- }
791
975
  }
792
- addComponentToNode(componentRef, nodeDOM) {
976
+ _addComponentToNode(componentRef, nodeDOM) {
793
977
  this.renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
794
978
  }
795
- createComponentReferenceWithJson(json) {
796
- const lessonCodedConfig = JSON.parse(json);
797
- let componentConfig;
798
- if (this.lesson.components && lessonCodedConfig.id) {
799
- // TODO: retomar, Creo que esta linea es para reutilziar los componentes por id, pero si no tiene,
800
- componentConfig = this.lesson.components.find((comp) => comp.id === lessonCodedConfig.id);
801
- }
802
- const LessonClass = getLessonComponentClass(lessonCodedConfig.component);
803
- if (!LessonClass) {
804
- console.error('No existe este componente, revisa el codigo insertado' + json);
805
- // throw new Error('No existe este com revisa el codigo insertado');
806
- return null;
979
+ _createComponentReferenceWithJson(json, currentLesson) {
980
+ try {
981
+ let lessonCodedConfig = JSON.parse(json);
982
+ debugger;
983
+ // Attempt to find pre-configured component data in the lesson object by ID
984
+ if (lessonCodedConfig.id && currentLesson?.dynamicComponents[lessonCodedConfig.id]) {
985
+ const foundConfig = currentLesson.dynamicComponents[lessonCodedConfig.id];
986
+ if (foundConfig) {
987
+ // Merge configurations: Start with coded, override/add with found lesson config
988
+ lessonCodedConfig = { ...lessonCodedConfig, ...foundConfig };
989
+ }
990
+ }
991
+ const LessonClass = getLessonComponentClass(lessonCodedConfig.component);
992
+ if (!LessonClass) {
993
+ console.error(`Component class not found for type: ${lessonCodedConfig.component}. JSON: ${json}`);
994
+ return null; // Return null if class doesn't exist
995
+ }
996
+ // Create component instance
997
+ const componentRef = this.viewContainerRef.createComponent(LessonClass);
998
+ if (lessonCodedConfig.inputs) {
999
+ for (const key in lessonCodedConfig.inputs) {
1000
+ if (lessonCodedConfig.inputs.hasOwnProperty(key)) {
1001
+ componentRef.instance[key] = lessonCodedConfig.inputs[key];
1002
+ }
1003
+ }
1004
+ }
1005
+ // i think i can improve this to pass only settings not all config, Assign the configuration to the component instance
1006
+ // settings data i defined in form interface, config will be all the component data including id and component type
1007
+ if (lessonCodedConfig.settings) {
1008
+ componentRef.instance.config = lessonCodedConfig;
1009
+ }
1010
+ return componentRef;
1011
+ }
1012
+ catch (error) {
1013
+ console.error(`Error processing component JSON: ${json}`, error);
1014
+ return null; // Return null on JSON parsing or other errors
807
1015
  }
808
- const componentRef = this.viewContainerRef.createComponent(LessonClass);
809
- componentRef.instance.config = componentConfig || lessonCodedConfig;
810
- return componentRef;
811
1016
  }
1017
+ // --- Evaluation Logic ---
812
1018
  async evaluateForms() {
1019
+ this.mainForm.markAllAsTouched(); // Mark all controls for validation feedback
813
1020
  if (!this.mainForm.valid) {
814
- for (let controlName of Object.keys(this.mainForm.controls)) {
815
- this.components[controlName].instance.validate();
816
- }
817
- this.toastrService.warn({ subtitle: 'Por favor completa todos los formularios', title: 'Espera' });
1021
+ // Trigger validation feedback on individual components visually if needed
1022
+ Object.keys(this.mainForm.controls).forEach((controlName) => {
1023
+ if (this.components[controlName]?.instance?.validate) {
1024
+ this.components[controlName].instance.validate(); // Assuming validate method handles visual feedback
1025
+ }
1026
+ });
1027
+ this.toastrService.warn({ subtitle: 'Por favor completa todos los ejercicios', title: 'Incompleto' });
818
1028
  return;
819
1029
  }
820
1030
  const rates = { correct: 0, incorrect: 0, score: 0 };
821
- for (let controlName of Object.keys(this.mainForm.controls)) {
822
- const instance = this.components[controlName].instance;
823
- if (instance instanceof ComponentWithForm) {
824
- // TODO: quizá no necesito validar instanceof ComponentWithForm ya que se garantiza que todo en mainform tiene un control.
1031
+ // Evaluate each component associated with a form control
1032
+ Object.keys(this.mainForm.controls).forEach((controlName) => {
1033
+ const instance = this.components[controlName]?.instance;
1034
+ // Check if the instance has an evaluate method (duck typing is okay here)
1035
+ if (instance && typeof instance.evaluate === 'function') {
825
1036
  try {
826
1037
  const result = instance.evaluate();
827
1038
  if (result) {
@@ -832,350 +1043,830 @@ class DCLessonRendererComponent {
832
1043
  }
833
1044
  }
834
1045
  catch (err) {
835
- console.error('Validation method for instance is failing', instance);
1046
+ console.error('Error during evaluation for component:', controlName, instance, err);
1047
+ rates.incorrect++; // Count errors as incorrect
836
1048
  }
837
1049
  }
1050
+ });
1051
+ if (this.test()) {
1052
+ // Access signal value
1053
+ console.log('Test mode: Evaluation skipped saving.');
1054
+ this.toastrService.info({ subtitle: `Test Results: ${rates.correct} correct, ${rates.incorrect} incorrect`, title: 'Test Mode' });
1055
+ return;
838
1056
  }
839
- if (this.test) {
1057
+ const totalQuestions = rates.correct + rates.incorrect;
1058
+ rates.score = totalQuestions > 0 ? rates.correct / totalQuestions : 0; // Avoid division by zero
1059
+ const status = rates.score >= 0.7 ? 'passed' : 'failed'; // Use >= for threshold
1060
+ const currentLesson = this.lesson();
1061
+ if (!currentLesson) {
1062
+ console.error('Cannot save result, lesson data is missing.');
1063
+ this.toastrService.error({ subtitle: 'Cannot save result, lesson data missing.', title: 'Error' });
840
1064
  return;
841
1065
  }
842
- rates.score = rates.correct / (rates.correct + rates.incorrect);
843
- const status = rates.score > 0.7 ? 'passed' : 'failed';
844
- const takenLesson = { lessonId: this.lesson.id, status: status, score: rates.score };
845
- // TODO: implementar el servicio para guardar la lección tomada
846
- // await this.lessonService.saveTakenLesson(takenLesson);
1066
+ const takenLesson = { lessonId: currentLesson.id, status: status, score: rates.score };
1067
+ console.log('Lesson evaluation result:', takenLesson);
1068
+ // TODO: Re-implement saving the taken lesson status via lessonService
1069
+ // try {
1070
+ // await this.lessonService.saveTakenLesson(takenLesson);
1071
+ // if (status === 'passed') {
1072
+ // this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Se guardó tu progreso.`, title: '¡Muy bien!' });
1073
+ // } else {
1074
+ // this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
1075
+ // }
1076
+ // } catch (error) {
1077
+ // console.error('Failed to save taken lesson', error);
1078
+ // this.toastrService.error({ subtitle: 'No se pudo guardar tu progreso.', title: 'Error' });
1079
+ // }
847
1080
  if (status === 'passed') {
848
- this.toastrService.success({ subtitle: 'Aprobaste, se guardó tu calificación', title: 'Muy bien' });
1081
+ this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
849
1082
  }
850
1083
  else {
851
- this.toastrService.warn({ subtitle: 'Revisa tus respuestas', title: 'Algunas preguntas no son correctas' });
1084
+ this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
852
1085
  }
853
1086
  }
1087
+ // --- AI Chat Logic ---
854
1088
  async startAI() {
855
- // TODO: para refactorizar y utilizar las lecciones en otro componente, voy a necesitar pasar esto al servicio abstracto asi cada aplicación lo implementa.
856
- const lessonText = this.lessonService.extractTextFromHtml(this.lesson.textCoded);
857
- const scenario = `The user is reading lessons through this app interface. They will now talk with you, and you need to evaluate their understanding of the lesson.
858
- Ask friendly questions throughout the conversation and help them learn English. Here is the lesson text the user just read:
859
- ${lessonText}
860
- In your next reply, start by greeting the user, asking something about the lesson, and then continue the conversation.`;
861
- // const user = this.userService.getUserSnapshot();
862
- alert('i need to fix this, need to find a way to pass user throug the service not the library, i think i already started something.');
863
- const user = {};
864
- const targetLevel = parseInt(user.languageProgress[user.settings.targetLanguage].level);
865
- const langTargetDesc = LangCodeDescription[user.settings.targetLanguage];
866
- const langBaseDesc = LangCodeDescription[user.settings.baseLanguage];
867
- let userInformationPrompt = `
868
- User information: user name is ${user.personalData.firstname} ${user.personalData.lastname}, their native language is ${langBaseDesc},
869
- and right now is learning ${langTargetDesc}, their current level is ${user.languageProgress[user.settings.targetLanguage].level} out of 5.`;
870
- if (targetLevel <= 2) {
871
- userInformationPrompt += `\nUser is a beginner in english, always reply in ${langBaseDesc}, but during the conversation use simple words in ${langTargetDesc}`;
1089
+ const currentLesson = this.lesson();
1090
+ if (!currentLesson) {
1091
+ console.error('Cannot start AI without a lesson.');
1092
+ this.toastrService.error({ subtitle: 'Lesson data not available.', title: 'Cannot Start Chat' });
1093
+ return;
1094
+ }
1095
+ console.log('Requesting agent cards from LessonAIService...');
1096
+ try {
1097
+ // Call the service to get the agent cards
1098
+ const agentCards = await this.lessonAIService.generateAgentCards(currentLesson);
1099
+ if (agentCards) {
1100
+ this.agentMasterLesson.set(agentCards.masterAgent);
1101
+ this.evaluatorAgentCard.set(agentCards.evaluatorAgent);
1102
+ this.chatVisible.set(true);
1103
+ console.log('Agent cards received and set.');
1104
+ }
1105
+ else {
1106
+ console.error('Failed to generate agent cards (service returned null).');
1107
+ this.toastrService.error({ subtitle: 'Could not prepare the AI chat session.', title: 'Error' });
1108
+ }
1109
+ }
1110
+ catch (error) {
1111
+ console.error('Error generating agent cards:', error);
1112
+ this.toastrService.error({ subtitle: 'An error occurred while preparing the AI chat.', title: 'Error' });
872
1113
  }
873
- console.log('lessonText', lessonText);
874
- const lessonAgentCard = DefaultLessonAgentCard;
875
- lessonAgentCard.characterCard.data.scenario = scenario;
876
- lessonAgentCard.characterCard.data.post_history_instructions += `\n${userInformationPrompt}`;
877
- this.agentMasterLesson = lessonAgentCard;
878
- this.evaluatorAgentCard = this.getDefualtLessonEvaluatorAgentCard(lessonText);
879
- this.chatVisible = true;
880
- }
881
- getDefualtLessonEvaluatorAgentCard(lessonText) {
882
- return {
883
- expectedResponseType: `interface EvalResult {
884
- score: number; // Score of the user's response 0 to 3
885
- feedback: string; // Feedback of the user's understanding of the conversation
886
- }`,
887
- messages: [],
888
- model: { id: 'gpt-4o-mini', provider: 'openai' },
889
- sources: [lessonText],
890
- task: `User is reading a taking a lesson, now their are having a conversation,
891
- you have to evaluate the current conversation, and give a feedback of the user understanding of the lesson,
892
- this is the lesson: ${lessonText}`,
893
- };
894
1114
  }
895
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonRendererComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }, { token: i1$1.ActivatedRoute }, { token: TOAST_ALERTS_TOKEN }, { token: LESSONS_TOKEN }, { token: CONVERSATION_AI_TOKEN }, { token: i2$4.DCConversationPromptBuilderService }], target: i0.ɵɵFactoryTarget.Component }); }
896
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lesson: "lesson", test: "test" }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, static: true }], ngImport: i0, template: "<div>\r\n <!-- <div *ngIf=\"lesson\">\r\n {lesson.title }} \r\n <br>\r\n <img [src]=\"imageCover\" alt=\"\">\r\n </div> -->\r\n\r\n <div #dynamicLesson class=\"targetclass\">\r\n <ng-template #target></ng-template>\r\n </div>\r\n</div>\r\n\r\n<br />\r\n<div style=\"display: flex; gap: 10px\">\r\n <div *ngIf=\"(mainForm.controls | keyvalue)?.length\">\r\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\r\n </div>\r\n\r\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\r\n</div>\r\n<br /><br />\r\n\r\n@if(chatVisible) {\r\n<p-drawer header=\"Conversation\" [(visible)]=\"chatVisible\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\r\n <dc-chat [agentCard]=\"agentMasterLesson\" [evaluatorAgentCard]=\"evaluatorAgentCard\"></dc-chat>\r\n</p-drawer>\r\n}\r\n", styles: [".evaluate{float:right}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "agentCard", "evaluatorAgentCard", "parseDict"], outputs: ["sendMessage"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4$1.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"] }] }); }
1115
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1116
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", 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 }, test: { classPropertyName: "test", publicName: "test", isSignal: true, isRequired: false, transformFunction: null } }, 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(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [agentCard]=\"agentMasterLesson()\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "agentCard", "evaluatorAgentCard", "parseDict"], outputs: ["sendMessage", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$4.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"] }] }); }
897
1117
  }
898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
1118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
899
1119
  type: Component,
900
- args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [NgIf, KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\r\n <!-- <div *ngIf=\"lesson\">\r\n {lesson.title }} \r\n <br>\r\n <img [src]=\"imageCover\" alt=\"\">\r\n </div> -->\r\n\r\n <div #dynamicLesson class=\"targetclass\">\r\n <ng-template #target></ng-template>\r\n </div>\r\n</div>\r\n\r\n<br />\r\n<div style=\"display: flex; gap: 10px\">\r\n <div *ngIf=\"(mainForm.controls | keyvalue)?.length\">\r\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\r\n </div>\r\n\r\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\r\n</div>\r\n<br /><br />\r\n\r\n@if(chatVisible) {\r\n<p-drawer header=\"Conversation\" [(visible)]=\"chatVisible\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\r\n <dc-chat [agentCard]=\"agentMasterLesson\" [evaluatorAgentCard]=\"evaluatorAgentCard\"></dc-chat>\r\n</p-drawer>\r\n}\r\n", styles: [".evaluate{float:right}\n"] }]
901
- }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ViewContainerRef }, { type: i1$1.ActivatedRoute }, { type: i5$1.ToastAlertsAbstractService, decorators: [{
902
- type: Inject,
903
- args: [TOAST_ALERTS_TOKEN]
904
- }] }, { type: LessonsAbstractService, decorators: [{
905
- type: Inject,
906
- args: [LESSONS_TOKEN]
907
- }] }, { type: i2$4.AgentCardsAbstractService, decorators: [{
908
- type: Inject,
909
- args: [CONVERSATION_AI_TOKEN]
910
- }] }, { type: i2$4.DCConversationPromptBuilderService }], propDecorators: { lesson: [{
911
- type: Input
912
- }], test: [{
913
- type: Input
914
- }], dynamicLesson: [{
1120
+ 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(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [agentCard]=\"agentMasterLesson()\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"] }]
1121
+ }], ctorParameters: () => [], propDecorators: { dynamicLesson: [{
915
1122
  type: ViewChild,
916
1123
  args: ['dynamicLesson', { static: true }]
917
1124
  }] } });
918
1125
 
919
- const GradientCss = 'linear-gradient(to bottom,var(--primary-color), rgba(213, 238, 239, 0.31))';
920
- class DCLessonEditorComponent {
921
- constructor(lessonService, toastService, notionService, _activatedRoute, router, cdr, dialogService) {
922
- this.lessonService = lessonService;
923
- this.toastService = toastService;
924
- this.notionService = notionService;
925
- this._activatedRoute = _activatedRoute;
926
- this.router = router;
927
- this.cdr = cdr;
928
- this.dialogService = dialogService;
929
- this.editor = BalloonEditor;
930
- this.coverStorageSettings = {
931
- path: 'lessons/covers',
932
- fileName: 'cover',
933
- cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
934
- };
935
- this.lessonComponentEnum = LessonComponentEnum;
936
- this.lessonId = this._activatedRoute.snapshot.paramMap.get('id');
937
- this.lesson = { textCoded: `<h1>Nueva lección </h1> <p> Texto aquí</p>`, tags: [] };
938
- this.components = {};
939
- this.isRendered = false;
940
- this.cover = '';
941
- this.isCropperVisible = false;
942
- this.isLoadingLesson = false;
1126
+ class LessonNotionService {
1127
+ constructor() {
1128
+ this.#notionService = inject(NOTION_SERVICE_TOKEN);
1129
+ this.#lessonService = inject(LESSONS_TOKEN);
1130
+ this.#toastService = inject(TOAST_ALERTS_TOKEN);
1131
+ // Keep track of loading state specific to Notion operations
1132
+ this.isLoading = signal(false);
1133
+ }
1134
+ #notionService;
1135
+ #lessonService;
1136
+ #toastService;
1137
+ /**
1138
+ * Extracts the Notion Page ID from a URL.
1139
+ * @param url The Notion page URL.
1140
+ * @returns The extracted page ID or null if invalid.
1141
+ */
1142
+ extractNotionPageId(url) {
1143
+ const notionIdRegex = /[a-f0-9]{32}(?=\?|$)/;
1144
+ const match = url.match(notionIdRegex);
1145
+ const notionId = match ? match[0] : null;
1146
+ if (!notionId) {
1147
+ this.#toastService.error({
1148
+ title: 'URL inválido',
1149
+ subtitle: 'Por favor ingresa una URL válida de Notion.',
1150
+ });
1151
+ return null;
1152
+ }
1153
+ return notionId;
943
1154
  }
944
- ngOnInit() {
945
- this.getLessonIfId();
946
- this.items = [
947
- {
948
- tooltipOptions: { tooltipLabel: 'Reparar con IA: Repara la lección con IA', tooltipPosition: 'bottom' },
949
- icon: 'pi pi-magic',
950
- command: () => this.generateByAI(),
951
- },
952
- {
953
- tooltipOptions: { tooltipLabel: 'Selector: Agrega un selector con multiples opciones', tooltipPosition: 'bottom' },
954
- icon: 'pi pi-caret-down',
955
- command: () => this.openComponentBuilder('selector'),
956
- },
957
- {
958
- tooltipOptions: { tooltipLabel: 'Hablar: Para que una palabra o frase sea reproducible', tooltipPosition: 'bottom' },
959
- icon: 'pi pi-megaphone',
960
- command: () => this.openComponentBuilder('speaker'),
961
- },
962
- {
963
- tooltipOptions: {
964
- tooltipLabel: 'Entrada de texto: Escribe una respuesta en un cuadro de texto',
965
- tooltipPosition: 'bottom',
966
- },
967
- icon: 'pi pi-pencil',
968
- command: () => this.openComponentBuilder('textWriter'),
969
- },
970
- {
971
- tooltipOptions: { tooltipLabel: 'Verbo: Para ver datos de un verbo', tooltipPosition: 'bottom' },
972
- icon: 'pi pi-eye',
973
- command: () => this.openComponentBuilder('verbSummary'),
974
- },
975
- {
976
- tooltipOptions: { tooltipLabel: 'Palabra: Para ver datos de una palabra', tooltipPosition: 'bottom' },
977
- icon: 'pi pi-file-word',
978
- command: () => this.openComponentBuilder('wordSummary'),
1155
+ /**
1156
+ * Links an existing lesson with a Notion page ID by updating the lesson's extras.
1157
+ * @param lesson The current lesson data.
1158
+ * @param notionPageId The Notion page ID to link.
1159
+ * @returns The updated lesson data from the backend or undefined on failure.
1160
+ */
1161
+ async linkLessonWithNotion(lesson, notionPageId) {
1162
+ if (!lesson || !lesson.id) {
1163
+ // Ensure lesson exists and has an ID
1164
+ this.#toastService.warn({ title: 'No se puede enlazar', subtitle: 'La lección debe existir y tener un ID.' });
1165
+ return undefined;
1166
+ }
1167
+ const updatedLesson = {
1168
+ ...lesson,
1169
+ extras: {
1170
+ ...(lesson.extras || {}),
1171
+ notionPageId: notionPageId,
979
1172
  },
980
- ];
981
- }
982
- async getLessonIfId() {
983
- if (!this.lessonId) {
984
- this.cover = `${GradientCss}, url("/assets/images/default_banner.webp")`;
985
- return;
1173
+ };
1174
+ this.isLoading.set(true);
1175
+ try {
1176
+ const savedLesson = await this.#lessonService.postLesson(updatedLesson);
1177
+ this.#toastService.success({ title: 'Listo', subtitle: 'Se enlazó la lección con Notion.' });
1178
+ return savedLesson;
1179
+ }
1180
+ catch (error) {
1181
+ // Remove explicit type, rely on tsconfig setting
1182
+ console.error('Error linking with Notion:', error);
1183
+ this.#toastService.error({ title: 'Error al enlazar', subtitle: 'Ocurrió un error inesperado.' });
1184
+ return undefined;
986
1185
  }
987
- this.lesson = await this.lessonService.getLesson(this.lessonId);
988
- console.log('lesson', this.lesson);
989
- if (!this.lesson) {
990
- this.toastService.warn({ title: 'No se encontró la lección', subtitle: 'Quiza el id es incorrecto' });
1186
+ finally {
1187
+ this.isLoading.set(false);
991
1188
  }
992
- this.togleRender();
993
- this.updateCover();
994
1189
  }
995
- updateCover() {
996
- const cover = this.lesson.media?.images?.find((img) => img.type === 'cover');
997
- if (cover) {
998
- this.cover = `${GradientCss}, url('${cover.url}')`;
1190
+ /**
1191
+ * Handles the process of importing lesson content from Notion.
1192
+ * It prompts the user for a URL if necessary, extracts the ID, fetches content,
1193
+ * and potentially links the lesson if it's an existing one.
1194
+ * @param currentLesson The current lesson data (can be a new or existing lesson).
1195
+ * @param lessonId The current lesson ID (null if it's a new lesson).
1196
+ * @returns The fetched HTML content from Notion, or null if the process fails.
1197
+ */
1198
+ async importAndLinkLessonFromNotion(currentLesson, lessonId) {
1199
+ if (!currentLesson)
1200
+ return null;
1201
+ let notionPageId = null;
1202
+ if (currentLesson.extras?.notionPageId) {
1203
+ const useExisting = confirm(`Ya tenemos el id ${currentLesson.extras.notionPageId} ¿Quieres usar este id para importar?`);
1204
+ if (useExisting) {
1205
+ notionPageId = currentLesson.extras.notionPageId;
1206
+ }
1207
+ else {
1208
+ const inputUrl = prompt('Ingresa la NUEVA URL de Notion para importar (este ID NO se guardará automáticamente si la lección ya existe)');
1209
+ if (!inputUrl)
1210
+ return null; // User cancelled
1211
+ notionPageId = this.extractNotionPageId(inputUrl);
1212
+ }
999
1213
  }
1000
1214
  else {
1001
- this.cover = `${GradientCss}, url("/assets/images/default_banner.webp")`;
1215
+ const inputUrl = prompt('Ingresa el URL de Notion para importar la lección (se enlazará si la lección ya existe)');
1216
+ if (!inputUrl)
1217
+ return null; // User cancelled
1218
+ notionPageId = this.extractNotionPageId(inputUrl);
1219
+ // Link automatically only if we got a valid Notion ID AND the lesson already exists (has an ID)
1220
+ if (notionPageId && lessonId) {
1221
+ const linkedLesson = await this.linkLessonWithNotion(currentLesson, notionPageId);
1222
+ if (!linkedLesson) {
1223
+ // Linking failed, maybe stop the import? Or proceed without linking?
1224
+ // For now, let's stop.
1225
+ this.#toastService.error({ title: 'Error de Enlace', subtitle: 'No se pudo enlazar con Notion antes de importar.' });
1226
+ return null;
1227
+ }
1228
+ // If linking succeeded, the lesson object might have changed, but we proceed with the import using the notionPageId.
1229
+ }
1002
1230
  }
1003
- this.cdr.detectChanges();
1004
- }
1005
- onTagRemove(tag) {
1006
- this.lesson.tags = this.lesson.tags.filter((text) => text !== tag.text);
1007
- }
1008
- onTagAdd(tag) {
1009
- if (tag.value) {
1010
- this.lesson.tags.push(tag.value);
1231
+ if (!notionPageId) {
1232
+ this.#toastService.warn({ title: 'Sin ID de Notion', subtitle: 'No se proporcionó un ID de Notion válido para importar.' });
1233
+ return null;
1234
+ }
1235
+ this.isLoading.set(true);
1236
+ try {
1237
+ this.#toastService.info({ title: 'Importando lección...', subtitle: 'Espera unos segundos' });
1238
+ const md = await this.#notionService.getPageInSpecificFormat(notionPageId, NotionExportType.HTML);
1239
+ console.log('Imported MD/HTML:', md);
1240
+ this.#toastService.success({ title: 'Contenido Importado', subtitle: 'Contenido de Notion obtenido.' });
1241
+ return md.content; // Return the fetched content
1242
+ }
1243
+ catch (error) {
1244
+ // Remove explicit type, rely on tsconfig setting
1245
+ console.error('Error importing from Notion:', error);
1246
+ this.#toastService.error({ title: 'Error de importación', subtitle: 'Ocurrió un error inesperado.' });
1247
+ return null; // Return null on failure
1248
+ }
1249
+ finally {
1250
+ this.isLoading.set(false);
1011
1251
  }
1012
- tag.input.nativeElement.value = '';
1013
1252
  }
1014
- async saveLesson(event = null) {
1015
- if (event) {
1016
- event.preventDefault();
1253
+ /**
1254
+ * Fetches content from the linked Notion page for AI improvement (placeholder).
1255
+ * @param lesson The current lesson data.
1256
+ */
1257
+ async improveLessonWithNotionAI(lesson) {
1258
+ if (!lesson)
1259
+ return;
1260
+ const notionId = lesson.extras?.notionPageId;
1261
+ if (!notionId) {
1262
+ this.#toastService.warn({ title: 'Sin ID de Notion', subtitle: 'Enlaza la lección con Notion primero.' });
1263
+ return;
1017
1264
  }
1018
- // TODO: Optimización importante, guardar una lección guarda todos los datos de nuevo, y no solo los que se modificaron, encontrar una forma de optimizar.
1019
- const lesson = await this.lessonService.postLesson(this.lesson);
1020
- if (!this.lessonId) {
1021
- this.toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
1022
- this.router.navigate([lesson.id], { relativeTo: this._activatedRoute });
1265
+ this.isLoading.set(true);
1266
+ try {
1267
+ this.#toastService.info({ title: 'Mejorando con IA...', subtitle: 'Obteniendo contenido de Notion.' });
1268
+ const md = await this.#notionService.getPageInSpecificFormat(notionId, NotionExportType.HTML);
1269
+ console.log('Content to improve:', md);
1270
+ // TODO: Add actual AI improvement logic here
1271
+ // e.g., call another service: await this.aiImprovementService.improve(md.content);
1272
+ // Then potentially update the lesson via lessonService or return data
1273
+ this.#toastService.success({ title: 'Contenido Obtenido', subtitle: 'Listo para mejorar con IA (lógica no implementada).' });
1023
1274
  }
1024
- else {
1025
- this.toastService.info({ title: 'Se guadarón los cambios en la lección', subtitle: 'Guardado' });
1026
- this.lesson = lesson;
1027
- this.validateAudios();
1275
+ catch (error) {
1276
+ // Remove explicit type, rely on tsconfig setting
1277
+ console.error('Error improving with AI:', error);
1278
+ this.#toastService.error({ title: 'Error de IA', subtitle: 'Ocurrió un error inesperado.' });
1279
+ }
1280
+ finally {
1281
+ this.isLoading.set(false);
1028
1282
  }
1029
- this.togleRender();
1030
- return lesson;
1031
1283
  }
1032
- async validateAudios() {
1033
- if (!this.lesson.components) {
1284
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonNotionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1285
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonNotionService }); }
1286
+ }
1287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonNotionService, decorators: [{
1288
+ type: Injectable
1289
+ }] });
1290
+
1291
+ class LessonUtilsService {
1292
+ #lessonService = inject(LESSONS_TOKEN);
1293
+ #toastService = inject(TOAST_ALERTS_TOKEN);
1294
+ constructor() { }
1295
+ /**
1296
+ * Validates if audios need generation for the given lesson.
1297
+ * Currently logs a placeholder message.
1298
+ * @param lesson The lesson object (or signal) to validate.
1299
+ */
1300
+ validateAudios(lesson) {
1301
+ // Access lesson data directly
1302
+ if (!lesson?.components) {
1034
1303
  return;
1035
1304
  }
1036
- // al menos un audio sin generar.
1037
- // agregar más condigciones después
1038
- // this.lesson.components.forEach((component: SpeakerCompConfiguration) => {
1039
- // if (component.component === 'speaker') {
1040
- // if (component.settings.voice && !component.audio) {
1041
- // // this.toastrService.warn('Se encontraron audios por generar', 'Será rápido');
1042
- // // TODO call backend.
1043
- // this.lessonService.generateAudiosForLesson(this.lesson.id).then((res) => {
1044
- // // this.toastrService.success('Se generaron los audios', 'Listo');
1045
- // console.log(res);
1046
- // });
1047
- // return;
1048
- // }
1049
- // }
1050
- // });
1305
+ // Placeholder logic - adapt as needed from original component
1306
+ console.log('Validating audios for lesson:', lesson.id);
1307
+ // Original logic commented out - requires SpeakerCompConfiguration type
1308
+ // const needsGeneration = lesson.components.some((component: any) => // Use 'any' or define SpeakerCompConfiguration
1309
+ // component.component === 'speaker' && component.settings?.voice && !component.audio
1310
+ // );
1311
+ // if (needsGeneration) {
1312
+ // this.#toastService.warn({ title: 'Audios por generar', subtitle: 'Se encontraron audios pendientes' });
1313
+ // // Consider calling generation service here or returning a flag
1314
+ // // this.#lessonService.generateAudiosForLesson(lesson.id).then(res => { ... });
1315
+ // }
1316
+ }
1317
+ /**
1318
+ * Updates the lesson signal with a new cover image.
1319
+ * @param lessonSignal The signal holding the lesson data.
1320
+ * @param imageUploaded The image data object from the upload event.
1321
+ */
1322
+ uploadCover(lessonSignal, imageUploaded) {
1323
+ // Use 'any' for now, refine if event structure is known
1324
+ const newImage = {
1325
+ type: 'cover',
1326
+ url: imageUploaded?.url,
1327
+ path: imageUploaded?.path,
1328
+ fullPath: imageUploaded?.fullPath,
1329
+ resolutions: imageUploaded?.resolutions ?? [],
1330
+ resolution: imageUploaded?.resolution,
1331
+ bucket: imageUploaded?.bucket,
1332
+ // Add any other required fields from LessonImage or ImgStorageData
1333
+ };
1334
+ lessonSignal.update((currentLesson) => {
1335
+ if (!currentLesson)
1336
+ return undefined;
1337
+ let updatedImages;
1338
+ const existingMedia = currentLesson.media;
1339
+ if (!existingMedia || !existingMedia.images) {
1340
+ updatedImages = [newImage];
1341
+ }
1342
+ else {
1343
+ const filteredImages = existingMedia.images.filter((img) => img.type !== 'cover');
1344
+ const mappedExistingImages = filteredImages.map((img) => ({
1345
+ type: img.type,
1346
+ url: img.url,
1347
+ path: img.path,
1348
+ fullPath: img.fullPath,
1349
+ resolutions: img.resolutions ?? [],
1350
+ resolution: img.resolution,
1351
+ bucket: img.bucket,
1352
+ }));
1353
+ updatedImages = [...mappedExistingImages, newImage];
1354
+ }
1355
+ return {
1356
+ ...currentLesson,
1357
+ media: {
1358
+ ...(existingMedia || {}),
1359
+ images: updatedImages,
1360
+ },
1361
+ };
1362
+ });
1363
+ // Note: Saving the lesson should be triggered from the component after calling this.
1364
+ }
1365
+ /**
1366
+ * Generates lesson content using AI. Assumes the lesson is already saved.
1367
+ * @param lessonId The ID of the lesson to generate content for.
1368
+ * @returns The updated lesson object after AI generation, or null on failure.
1369
+ */
1370
+ async generateByAI(lessonId) {
1371
+ if (!lessonId) {
1372
+ this.#toastService.warn({ title: 'ID Requerido', subtitle: 'Se necesita un ID de lección para usar IA.' });
1373
+ return null;
1374
+ }
1375
+ // No need to save here, component should ensure it's saved before calling.
1376
+ try {
1377
+ await this.#lessonService.postGenerateByAI(lessonId);
1378
+ // Re-fetch the lesson data to get AI updates
1379
+ const updatedLesson = await this.#lessonService.getLesson(lessonId);
1380
+ if (updatedLesson) {
1381
+ this.#toastService.success({ title: 'IA completada', subtitle: 'Lección actualizada con IA.' });
1382
+ return updatedLesson;
1383
+ }
1384
+ else {
1385
+ throw new Error('Failed to fetch lesson after AI generation');
1386
+ }
1387
+ }
1388
+ catch (error) {
1389
+ // Type the error object
1390
+ console.error('Error during AI generation in service:', error);
1391
+ this.#toastService.error({ title: 'Error de IA', subtitle: 'No se pudo generar la lección con IA.' });
1392
+ return null; // Return null in catch block
1393
+ }
1394
+ // Loading state should be managed by the component calling this service.
1395
+ }
1396
+ /**
1397
+ * Cleans orphaned components from the lesson's dynamicComponents.
1398
+ * An orphaned component is one present in dynamicComponents but its ID is not found in the textCoded HTML.
1399
+ * @param lesson The lesson object to clean.
1400
+ * @returns A new lesson object with orphaned components removed from dynamicComponents.
1401
+ */
1402
+ cleanOrphanedComponents(lesson) {
1403
+ if (!lesson || !lesson.textCoded || !lesson.dynamicComponents) {
1404
+ // Return the original lesson if essential parts are missing
1405
+ return lesson;
1406
+ }
1407
+ const textCoded = lesson.textCoded;
1408
+ const existingComponents = lesson.dynamicComponents;
1409
+ const existingComponentIds = new Set(Object.keys(existingComponents));
1410
+ // Regex to find "id":"<component_id>" within the textCoded string
1411
+ const idRegex = /"id":"([^"]+)"/g;
1412
+ const foundIdsInText = new Set();
1413
+ let match;
1414
+ while ((match = idRegex.exec(textCoded)) !== null) {
1415
+ const potentialId = match[1];
1416
+ // Check if the found ID actually exists in our dynamic components map
1417
+ // This ensures we only count IDs that correspond to known components.
1418
+ if (existingComponentIds.has(potentialId)) {
1419
+ foundIdsInText.add(potentialId);
1420
+ }
1421
+ }
1422
+ const orphanedIds = [];
1423
+ const cleanedDynamicComponents = {};
1424
+ // Iterate through existing component IDs
1425
+ for (const componentId of existingComponentIds) {
1426
+ if (foundIdsInText.has(componentId)) {
1427
+ // Keep the component if its ID was found in the text
1428
+ cleanedDynamicComponents[componentId] = existingComponents[componentId];
1429
+ }
1430
+ else {
1431
+ // Mark as orphaned if not found
1432
+ orphanedIds.push(componentId);
1433
+ }
1434
+ }
1435
+ // Log a warning if any orphaned components were found
1436
+ if (orphanedIds.length > 0) {
1437
+ console.warn(`[LessonUtilsService] Orphaned components detected and will be removed from lesson data (IDs not found in textCoded):`, orphanedIds);
1438
+ this.#toastService.warn({
1439
+ title: 'Componentes Huérfanos Detectados',
1440
+ subtitle: `Se removerán ${orphanedIds.length} componentes no usados del editor.`,
1441
+ // life: 5000, // Removed 'life' property as it might not be supported by ToastData
1442
+ });
1443
+ }
1444
+ // Return a new lesson object with the cleaned components
1445
+ return {
1446
+ ...lesson,
1447
+ dynamicComponents: cleanedDynamicComponents,
1448
+ };
1051
1449
  }
1052
- togleRender(_event = null) {
1053
- this.isRendered = false;
1054
- setTimeout(() => {
1055
- this.isRendered = true;
1056
- this.cdr.detectChanges();
1057
- }, 400);
1058
- this.cdr.detectChanges();
1450
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonUtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1451
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonUtilsService, providedIn: 'root' }); }
1452
+ }
1453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonUtilsService, decorators: [{
1454
+ type: Injectable,
1455
+ args: [{
1456
+ providedIn: 'root', // Provide globally or in a specific module if preferred
1457
+ }]
1458
+ }], ctorParameters: () => [] });
1459
+
1460
+ class DCLessonComponentAdderComponent {
1461
+ constructor() {
1462
+ // Services
1463
+ this.#dialogService = inject(DialogService);
1464
+ this.#toastService = inject(TOAST_ALERTS_TOKEN);
1465
+ this.componentAdded = new EventEmitter(); // Changed Output name and type
1466
+ // Expose enum to the template
1467
+ this.lessonComponentEnum = LessonComponentEnum;
1059
1468
  }
1469
+ // Services
1470
+ #dialogService;
1471
+ #toastService;
1472
+ // Moved logic from DCLessonEditorComponent
1060
1473
  openComponentBuilder(type) {
1061
- return this.dialogService.open(LessonComponentBuilders[type], { width: '550px', header: 'Agregar componente', closable: true }).onClose.pipe();
1474
+ const componentToBuild = LessonComponentBuilders[type];
1475
+ if (!componentToBuild) {
1476
+ console.error(`No component builder found for type: ${type}`);
1477
+ this.#toastService.error({ title: 'Error', subtitle: `Componente desconocido: ${type}` });
1478
+ return;
1479
+ }
1480
+ const dialogRef = this.#dialogService.open(componentToBuild, {
1481
+ width: '80vw',
1482
+ header: 'Agregar componente',
1483
+ closable: true,
1484
+ });
1485
+ // Handle the result and emit the new event
1486
+ dialogRef.onClose.subscribe((result) => {
1487
+ if (result) {
1488
+ console.log('Component builder closed:', result);
1489
+ this.componentAdded.emit(result); // Emit the result when dialog closes successfully
1490
+ }
1491
+ });
1492
+ }
1493
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonComponentAdderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1494
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", 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 <!-- 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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
1495
+ }
1496
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonComponentAdderComponent, decorators: [{
1497
+ type: Component,
1498
+ args: [{ selector: 'dc-lesson-component-adder', standalone: true, imports: [CommonModule, ButtonModule, TooltipModule], providers: [DialogService], 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 <!-- Add other buttons here if needed, following the same pattern -->\n</div>\n" }]
1499
+ }], propDecorators: { componentAdded: [{
1500
+ type: Output
1501
+ }] } });
1502
+
1503
+ class DCLessonMetadataEditorComponent {
1504
+ constructor() {
1505
+ // Use signal for input lesson data
1506
+ this.lesson = signal(undefined);
1507
+ this.isLoadingLesson = signal(false);
1508
+ // Outputs for actions
1509
+ this.saveRequest = new EventEmitter();
1510
+ this.importNotionRequest = new EventEmitter();
1511
+ this.improveNotionRequest = new EventEmitter();
1512
+ this.generateAIRequest = new EventEmitter();
1513
+ // Output for property changes
1514
+ this.propertyChange = new EventEmitter();
1515
+ }
1516
+ // Method to emit property changes, called by ngModelChange in the template
1517
+ onPropertyChange(property, value) {
1518
+ this.propertyChange.emit({ property, value });
1519
+ }
1520
+ // Methods to emit action requests
1521
+ emitSaveRequest() {
1522
+ this.saveRequest.emit();
1523
+ }
1524
+ emitImportNotionRequest() {
1525
+ this.importNotionRequest.emit();
1526
+ }
1527
+ emitImproveNotionRequest() {
1528
+ this.improveNotionRequest.emit();
1529
+ }
1530
+ emitGenerateAIRequest() {
1531
+ this.generateAIRequest.emit();
1532
+ }
1533
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1534
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest", generateAIRequest: "generateAIRequest", propertyChange: "propertyChange" }, ngImport: i0, template: "@if (lesson(); as currentLesson) {\n<div>\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\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.title\"\n (ngModelChange)=\"onPropertyChange('title', $event)\"\n type=\"text\"\n placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.description\"\n (ngModelChange)=\"onPropertyChange('description', $event)\"\n type=\"text\"\n placeholder=\"Agrega una descripci\u00F3n\" />\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.prompt\"\n (ngModelChange)=\"onPropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" [disabled]=\"isLoadingLesson()\" (click)=\"emitGenerateAIRequest()\"> Generar con IA </p-button>\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.isPublished\"\n (ngModelChange)=\"onPropertyChange('isPublished', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n\n <input\n pInputText\n [ngModel]=\"currentLesson.level\"\n (ngModelChange)=\"onPropertyChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n </div>\n\n <!-- Access signal values -->\n <div style=\"margin-top: 10px; font-size: 0.9em; color: var(--text-color-secondary)\">\n {{ currentLesson.baseLang | flagEmoji }} -> {{ currentLesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.baseLang | langDesc : 'es' }} que aprenden\n {{ currentLesson.targetLang | langDesc : 'es' }}\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.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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "pipe", type: // Added TooltipModule
1535
+ FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
1536
+ LangDescTranslationPipe, name: "langDesc" }] }); }
1537
+ }
1538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, decorators: [{
1539
+ type: Component,
1540
+ args: [{ selector: 'dc-lesson-metadata-editor', standalone: true, imports: [
1541
+ CommonModule,
1542
+ FormsModule,
1543
+ ButtonModule,
1544
+ InputTextModule,
1545
+ TooltipModule, // Added TooltipModule
1546
+ FlagLanguagePipe, // Added Pipe
1547
+ LangDescTranslationPipe, // Added Pipe
1548
+ ], template: "@if (lesson(); as currentLesson) {\n<div>\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\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.title\"\n (ngModelChange)=\"onPropertyChange('title', $event)\"\n type=\"text\"\n placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.description\"\n (ngModelChange)=\"onPropertyChange('description', $event)\"\n type=\"text\"\n placeholder=\"Agrega una descripci\u00F3n\" />\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.prompt\"\n (ngModelChange)=\"onPropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" [disabled]=\"isLoadingLesson()\" (click)=\"emitGenerateAIRequest()\"> Generar con IA </p-button>\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.isPublished\"\n (ngModelChange)=\"onPropertyChange('isPublished', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n\n <input\n pInputText\n [ngModel]=\"currentLesson.level\"\n (ngModelChange)=\"onPropertyChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n </div>\n\n <!-- Access signal values -->\n <div style=\"margin-top: 10px; font-size: 0.9em; color: var(--text-color-secondary)\">\n {{ currentLesson.baseLang | flagEmoji }} -> {{ currentLesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.baseLang | langDesc : 'es' }} que aprenden\n {{ currentLesson.targetLang | langDesc : 'es' }}\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" }]
1549
+ }], propDecorators: { lesson: [{
1550
+ type: Input,
1551
+ args: [{ required: true }]
1552
+ }], isLoadingLesson: [{
1553
+ type: Input,
1554
+ args: [{ required: true }]
1555
+ }], saveRequest: [{
1556
+ type: Output
1557
+ }], importNotionRequest: [{
1558
+ type: Output
1559
+ }], improveNotionRequest: [{
1560
+ type: Output
1561
+ }], generateAIRequest: [{
1562
+ type: Output
1563
+ }], propertyChange: [{
1564
+ type: Output
1565
+ }] } });
1566
+
1567
+ const GradientCss = 'linear-gradient(to bottom,var(--primary-color), rgba(213, 238, 239, 0.31))';
1568
+ class DCLessonEditorComponent {
1569
+ // Services
1570
+ #activatedRoute; // Re-inject as it's needed for navigation
1571
+ #lessonNotionService;
1572
+ #lessonUtilsService;
1573
+ #router;
1574
+ // Removed #dialogService injection
1575
+ #lessonService;
1576
+ #toastService;
1577
+ constructor() {
1578
+ // Removed Speed Dial Items initialization
1579
+ // Services
1580
+ this.#activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
1581
+ this.#lessonNotionService = inject(LessonNotionService);
1582
+ this.#lessonUtilsService = inject(LessonUtilsService);
1583
+ this.#router = inject(Router);
1584
+ // Removed #dialogService injection
1585
+ this.#lessonService = inject(LESSONS_TOKEN);
1586
+ this.#toastService = inject(TOAST_ALERTS_TOKEN);
1587
+ // Signals States
1588
+ this.lessonId = toSignal(inject(ActivatedRoute).paramMap.pipe(map((params) => params.get('id'))));
1589
+ this.lesson = signal(undefined); // Initialize as undefined
1590
+ this.isCropperVisible = signal(false);
1591
+ this.isLoadingLesson = signal(false);
1592
+ // Removed items signal
1593
+ // Computed Signals
1594
+ this.coverBackground = computed(() => {
1595
+ const currentLesson = this.lesson();
1596
+ const coverImage = currentLesson?.media?.images?.find((img) => img.type === 'cover');
1597
+ const imageUrl = coverImage?.url || '/assets/images/default_banner.webp';
1598
+ return `${GradientCss}, url("${imageUrl}")`;
1599
+ });
1600
+ // Computed signal to get dynamic components as an array for easier iteration in the template
1601
+ this.dynamicComponentsArray = computed(() => {
1602
+ const currentLesson = this.lesson();
1603
+ if (currentLesson?.dynamicComponents) {
1604
+ return Object.values(currentLesson.dynamicComponents);
1605
+ }
1606
+ return []; // Return empty array if no lesson or no dynamic components
1607
+ });
1608
+ // States
1609
+ this.components = {}; // Current Dynamic components
1610
+ this.editor = BalloonEditor;
1611
+ this.lessonComponentEnum = LessonComponentEnum;
1612
+ this.coverStorageSettings = {
1613
+ path: 'lessons/covers',
1614
+ fileName: 'cover',
1615
+ cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
1616
+ };
1617
+ // Effect to fetch lesson data when ID changes
1618
+ effect(async () => {
1619
+ const id = this.lessonId();
1620
+ console.log('Lesson ID Signal:', id);
1621
+ if (id) {
1622
+ this.isLoadingLesson.set(true); // Start loading
1623
+ try {
1624
+ const fetchedLesson = await this.#lessonService.getLesson(id);
1625
+ if (fetchedLesson) {
1626
+ this.lesson.set(fetchedLesson);
1627
+ }
1628
+ else {
1629
+ this.lesson.set(undefined); // Reset if not found
1630
+ this.#toastService.warn({ title: 'No se encontró la lección', subtitle: 'Quizá el id es incorrecto' });
1631
+ // Optional: Navigate away or show a specific "not found" state
1632
+ // this.#router.navigate(['/path/to/lessons']);
1633
+ }
1634
+ }
1635
+ catch (error) {
1636
+ console.error('Error fetching lesson:', error);
1637
+ this.lesson.set(undefined); // Reset on error
1638
+ this.#toastService.error({ title: 'Error al cargar la lección', subtitle: 'Intenta de nuevo más tarde' });
1639
+ }
1640
+ finally {
1641
+ this.isLoadingLesson.set(false); // Stop loading
1642
+ }
1643
+ }
1644
+ else {
1645
+ // Handle case for new lesson (ID is null/undefined)
1646
+ this.lesson.set({ textCoded: `<h1>Nueva lección </h1> <p> Texto aquí</p>`, tags: [] }); // Set default new lesson structure
1647
+ this.isLoadingLesson.set(false); // Ensure loading is off
1648
+ }
1649
+ });
1650
+ }
1651
+ /**
1652
+ * Updates a specific property on the lesson signal.
1653
+ * Used for ngModelChange events to simulate two-way binding with signals.
1654
+ * @param property The key of the ILesson property to update.
1655
+ * @param value The new value for the property.
1656
+ */
1657
+ updateLessonProperty(property, value) {
1658
+ this.lesson.update((currentLesson) => {
1659
+ if (!currentLesson)
1660
+ return undefined;
1661
+ return { ...currentLesson, [property]: value };
1662
+ });
1663
+ }
1664
+ onTagRemove(tag) {
1665
+ this.lesson.update((currentLesson) => {
1666
+ if (!currentLesson)
1667
+ return undefined;
1668
+ const updatedTags = currentLesson.tags.filter((text) => text !== tag.text);
1669
+ return { ...currentLesson, tags: updatedTags };
1670
+ });
1062
1671
  }
1063
- uploadCover(imageUploaded) {
1064
- const image = { type: 'cover', ...imageUploaded };
1065
- if (!this.lesson.media) {
1066
- // puede que no exista media
1067
- this.lesson.media = {};
1068
- this.lesson.media.images = [image];
1672
+ onTagAdd(tag) {
1673
+ if (tag.value) {
1674
+ this.lesson.update((currentLesson) => {
1675
+ if (!currentLesson)
1676
+ return undefined;
1677
+ // Avoid duplicate tags if necessary
1678
+ if (currentLesson.tags.includes(tag.value)) {
1679
+ return currentLesson;
1680
+ }
1681
+ const updatedTags = [...currentLesson.tags, tag.value];
1682
+ return { ...currentLesson, tags: updatedTags };
1683
+ });
1069
1684
  }
1070
- else {
1071
- // solo sustituir el cover si ya existe, como siempre utilizo el mismo nombre, no tengo que eliminar la anterior si actualizo.
1072
- // const currentCover = this.lesson.media.images.find((img) => img.type === 'cover');
1073
- this.lesson.media.images = this.lesson.media.images.filter((img) => img.type !== 'cover');
1074
- this.lesson.media.images.push(image);
1685
+ tag.input.nativeElement.value = ''; // Clear input
1686
+ }
1687
+ async saveLesson(event) {
1688
+ event?.preventDefault();
1689
+ const currentLesson = this.lesson();
1690
+ if (!currentLesson) {
1691
+ this.#toastService.error({ title: 'Error', subtitle: 'No hay datos de lección para guardar' });
1692
+ return undefined;
1693
+ }
1694
+ // Clean orphaned components before saving
1695
+ const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLesson);
1696
+ // TODO: Implement optimization for saving only changed data.
1697
+ // This requires comparing lessonToSave with the initially fetched state.
1698
+ this.isLoadingLesson.set(true); // Indicate saving
1699
+ try {
1700
+ // Use the cleaned lesson object for saving
1701
+ const savedLesson = await this.#lessonService.postLesson(lessonToSave);
1702
+ const currentId = this.lessonId();
1703
+ if (!currentId) {
1704
+ // It was a new lesson, now it has an ID. Navigate.
1705
+ this.#toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
1706
+ // The effect should automatically fetch the lesson again after navigation due to paramMap change.
1707
+ this.#router.navigate(['../', savedLesson.id], { relativeTo: this.#activatedRoute });
1708
+ }
1709
+ else {
1710
+ // It was an existing lesson, update the signal with the potentially updated data from the backend.
1711
+ this.lesson.set(savedLesson);
1712
+ this.#toastService.info({ title: 'Se guardaron los cambios en la lección', subtitle: 'Guardado' });
1713
+ // Call the service method for validation
1714
+ this.#lessonUtilsService.validateAudios(this.lesson());
1715
+ }
1716
+ return savedLesson;
1717
+ }
1718
+ catch (error) {
1719
+ // Type error
1720
+ console.error('Error saving lesson:', error);
1721
+ this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudieron guardar los cambios' });
1722
+ return undefined;
1723
+ }
1724
+ finally {
1725
+ this.isLoadingLesson.set(false); // Finish saving indication
1726
+ }
1727
+ } // Add missing closing brace for saveLesson
1728
+ // Removed openComponentBuilder method
1729
+ /**
1730
+ * Handles the event emitted when a component is added via the adder component.
1731
+ * @param result The configuration data returned from the component builder dialog. Expected format: { obj: LessonComponentConfiguration }
1732
+ */
1733
+ onComponentAdded(result) {
1734
+ debugger;
1735
+ // Check if result and result.obj.id exist
1736
+ const newComponent = result?.obj;
1737
+ if (newComponent?.id) {
1738
+ console.log('Component builder closed, result received in editor:', newComponent);
1739
+ // // Transform LessonComponentConfiguration to DynamicContentComponent
1740
+ // const dynamicComponentToAdd: DynamicContentComponent = {
1741
+ // id: componentConfig.id,
1742
+ // component: componentConfig.component,
1743
+ // inputs: {
1744
+ // config: componentConfig, // Pass the original config object as an input named 'config'
1745
+ // // Add other potential inputs if needed based on component type later
1746
+ // },
1747
+ // };
1748
+ // Update the lesson signal, adding the transformed component to the dynamicComponents object
1749
+ this.lesson.update((currentLesson) => {
1750
+ if (!currentLesson)
1751
+ return undefined;
1752
+ // Ensure dynamicComponents object exists, initialize if not
1753
+ const currentDynamicComponents = currentLesson.dynamicComponents || {};
1754
+ // Create the updated dynamicComponents object
1755
+ const updatedDynamicComponents = {
1756
+ ...currentDynamicComponents,
1757
+ [newComponent.id]: newComponent, // Use component's id as the key
1758
+ };
1759
+ // Return the updated lesson state
1760
+ return { ...currentLesson, dynamicComponents: updatedDynamicComponents };
1761
+ });
1762
+ // Optionally save the lesson after adding the component
1763
+ // this.saveLesson();
1075
1764
  }
1076
- this.updateCover();
1077
- this.saveLesson();
1078
1765
  }
1079
1766
  openCropper() {
1080
- this.isCropperVisible = true;
1767
+ // Correctly define openCropper
1768
+ this.isCropperVisible.set(true);
1081
1769
  }
1770
+ // isLoadingLesson signal is used directly
1082
1771
  async generateByAI() {
1083
- this.isLoadingLesson = true;
1772
+ const currentId = this.lessonId();
1773
+ if (!currentId) {
1774
+ this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
1775
+ return;
1776
+ }
1777
+ this.isLoadingLesson.set(true);
1084
1778
  try {
1085
- await this.saveLesson();
1086
- await this.lessonService.postGenerateByAI(this.lesson.id);
1087
- await this.getLessonIfId();
1779
+ // Ensure latest changes are saved before generating
1780
+ const savedLesson = await this.saveLesson();
1781
+ if (!savedLesson) {
1782
+ // Handle save error - toast is shown in saveLesson
1783
+ throw new Error('Failed to save before AI generation');
1784
+ }
1785
+ // Call the service method
1786
+ const updatedLesson = await this.#lessonUtilsService.generateByAI(currentId);
1787
+ if (updatedLesson) {
1788
+ this.lesson.set(updatedLesson); // Update the signal with AI changes from service
1789
+ // Toast success is handled by the service
1790
+ }
1791
+ else {
1792
+ // Toast error is handled by the service
1793
+ throw new Error('AI generation failed or lesson fetch failed after generation.');
1794
+ }
1795
+ }
1796
+ catch (error) {
1797
+ // Type error
1798
+ console.error('Error during AI generation process in component:', error);
1799
+ // Service handles specific AI error toasts, maybe add a general one here if needed
1800
+ // this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
1088
1801
  }
1089
1802
  finally {
1090
- this.isLoadingLesson = false;
1803
+ this.isLoadingLesson.set(false); // Stop loading
1091
1804
  }
1092
1805
  }
1093
- onImageSelected(event) {
1094
- console.log(event);
1095
- }
1806
+ /**
1807
+ * Handles the image upload event, updates the lesson signal via the service, and saves.
1808
+ * @param event The image upload event data.
1809
+ */
1096
1810
  async onImageUploaded(event) {
1097
- this.uploadCover(event);
1098
- this.cdr.detectChanges();
1099
- }
1811
+ // Call the service to update the signal
1812
+ this.#lessonUtilsService.uploadCover(this.lesson, event);
1813
+ // The coverBackground computed signal will update automatically.
1814
+ // Save the lesson after the signal has been updated
1815
+ await this.saveLesson();
1816
+ }
1817
+ /**
1818
+ * Imports lesson content from Notion using the LessonNotionService.
1819
+ */
1100
1820
  async importFromNotion() {
1101
- let notionPageId;
1102
- if (this.lesson?.extras?.notionPageId) {
1103
- const response = confirm(`Ya tenemos el id ${this.lesson.extras?.notionPageId} ¿Quieres usar este id?`);
1104
- if (!response) {
1105
- return;
1821
+ // Use the service's loading state or manage locally
1822
+ this.isLoadingLesson.set(true);
1823
+ try {
1824
+ const newContent = await this.#lessonNotionService.importAndLinkLessonFromNotion(this.lesson(), this.lessonId());
1825
+ if (newContent !== null) {
1826
+ // Update the lesson signal's textCoded property
1827
+ this.updateLessonProperty('textCoded', newContent);
1828
+ // Toast success is handled within the service now
1106
1829
  }
1107
- notionPageId = this.lesson.extras?.notionPageId;
1830
+ // If newContent is null, the service handled errors/toasts
1108
1831
  }
1109
- else {
1110
- const response = prompt('Ingresa el url de notion para importar la lección, se guardará este id');
1111
- if (!response) {
1112
- return;
1113
- }
1114
- notionPageId = this.extractNotionPageId(response);
1115
- if (!notionPageId)
1116
- return;
1117
- this.linkWithNotion(notionPageId);
1118
- }
1119
- this.toastService.info({ title: 'Importando lección...', subtitle: 'Espera unos segundos' });
1120
- const md = await this.notionService.getPageInSpecificFormat(notionPageId, NotionExportType.HTML);
1121
- console.log(md);
1122
- this.lesson.textCoded = md.content;
1123
- this.togleRender();
1124
- this.toastService.success({ title: 'Listo', subtitle: 'La lección se importó correctamente' });
1125
- }
1126
- extractNotionPageId(url) {
1127
- const notionIdRegex = /[a-f0-9]{32}(?=\?|$)/;
1128
- const match = url.match(notionIdRegex);
1129
- const notionId = match ? match[0] : null;
1130
- if (!notionId) {
1131
- this.toastService.error({
1132
- title: 'URL inválido',
1133
- subtitle: 'Por favor ingresa una URL válida de Notion',
1134
- });
1832
+ finally {
1833
+ // Ensure loading state is reset regardless of service outcome
1834
+ // If observing service state: this.isLoadingLesson.set(this.#lessonNotionService.isLoading());
1835
+ this.isLoadingLesson.set(false); // Keep local loading for now
1135
1836
  }
1136
- return notionId;
1137
- }
1138
- async linkWithNotion(notionPageId) {
1139
- const extra = this.lesson.extras || {};
1140
- extra.notionPageId = notionPageId;
1141
- await this.lessonService.postLesson({ ...this.lesson, extras: extra });
1142
- this.toastService.success({ title: 'Listo', subtitle: 'Se enlazó la lección con Notion' });
1143
1837
  }
1838
+ /**
1839
+ * Calls the LessonNotionService to improve the lesson using AI based on Notion content.
1840
+ */
1144
1841
  async improveNotionWithAI() {
1145
- const md = await this.notionService.getPageInSpecificFormat(this.lesson.extras.notionPageId, NotionExportType.HTML);
1146
- console.log(md);
1147
- // this.lesson.textCoded = md.content;
1148
- // this.togleRender();
1149
- // this.toastService.success({ title: 'Listo', subtitle: 'La lección se mejoró con AI' });
1150
- }
1151
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonEditorComponent, deps: [{ token: LESSONS_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: NOTION_SERVICE_TOKEN }, { token: i1$1.ActivatedRoute }, { token: i1$1.Router }, { token: i0.ChangeDetectorRef }, { token: i2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
1152
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", 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 <div class=\"header-cover\" [style.background-image]=\"cover\"></div>\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</div>\n\n<br />\n\n<div>\n <div>\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"saveLesson()\" />\n\n <p-button label=\"Previsualizar\" severity=\"secondary\" (click)=\"togleRender()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"importFromNotion()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"improveNotionWithAI()\" />\n </div>\n\n <div>\n <input pInputText style=\"width: 100%\" [(ngModel)]=\"lesson.title\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <input pInputText style=\"width: 100%\" [(ngModel)]=\"lesson.description\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n </div>\n\n <div style=\"display: flex; align-items: center\">\n <br />\n <br />\n <input pInputText style=\"flex: auto\" [(ngModel)]=\"lesson.prompt\" type=\"text\" placeholder=\"Prompt\" />\n <p-button severity=\"primary\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\"> Generar </p-button>\n </div>\n\n <div>\n <label class=\"checkbox-container\">\n <input type=\"checkbox\" [(ngModel)]=\"lesson.isPublished\" title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Lecci\u00F3n publicada\n </label>\n <br />\n\n <input pInputText [(ngModel)]=\"lesson.level\" type=\"number\" placeholder=\"Level\" />\n </div>\n\n <div>\n {{ lesson.baseLang | flagEmoji }} -> {{ lesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de {{ lesson.baseLang | langDesc : 'es' }} que aprenden\n {{ lesson.targetLang | langDesc : 'es' }}\n </div>\n </div>\n </div>\n\n <hr />\n <span>Componentes: </span>\n <div style=\"display: flex; gap: 10px\">\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.Selector)\" pTooltip=\"Agrega un selector con multiples opciones\">\n Selector\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.Speaker)\" pTooltip=\"Para que una palabra o frase sea reproducible\">\n Speaker\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.TextWriter)\" pTooltip=\"Escribe una respuesta en un cuadro de texto\">\n Text\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\" pTooltip=\"Muestra la informaci\u00F3n de un verbo\"> Text </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\" pTooltip=\"Muestra la informaci\u00F3n de un verbo\"> Verb </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.WordSummary)\" pTooltip=\"Muestra la informaci\u00F3n de una palabra\">\n Palabra\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.TranslationSwitcher)\" pTooltip=\"Muestra el texto pero al pica cambia de idioma\">\n Traducci\u00F3n\n </p-button>\n </div>\n\n <hr />\n\n <p-splitter [style]=\"{ height: '80vh' }\" styleClass=\"mb-8\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n (keydown.control.space)=\"togleRender($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [(ngModel)]=\"lesson.textCoded\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n @if (isRendered) {\n <dc-lesson-renderer class=\"text-editor\" [lesson]=\"lesson\" [test]=\"true\"></dc-lesson-renderer>\n }\n </ng-template>\n </p-splitter>\n\n <div class=\"float-button\">\n <p-speeddial [model]=\"items\" [radius]=\"120\" type=\"quarter-circle\" direction=\"up-left\" />\n\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</div>\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.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;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.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: 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i4$2.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i6.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i8.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "style", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lesson", "test"] }, { kind: "pipe", type: LangDescTranslationPipe, name: "langDesc" }, { kind: "pipe", type: FlagLanguagePipe, name: "flagEmoji" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i9.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$2.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"] }] }); }
1842
+ await this.#lessonNotionService.improveLessonWithNotionAI(this.lesson());
1843
+ }
1844
+ showComponentDetails(data) {
1845
+ alert('showComponentDetails' + JSON.stringify(data));
1846
+ }
1847
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1848
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", 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 <div class=\"header-cover\" [style.background-image]=\"coverBackground()\"></div>\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</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (generateAIRequest)=\"generateByAI()\"\n (propertyChange)=\"updateLessonProperty($event.property, $event.value)\">\n</dc-lesson-metadata-editor>\n\n<hr />\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>ID: {{ comp.id }} - Tipo: {{ comp.component }}<button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button></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' }\" styleClass=\"mb-8\">\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()\" [test]=\"true\"></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", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.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;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.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", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$6.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: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lessonInput", "lessonIdInput", "test"] }, { 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: SplitterModule }, { kind: "component", type: i5$1.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "style", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type:
1849
+ // Removed SpeedDialModule
1850
+ DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "component", type: // Add the component adder here
1851
+ DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "generateAIRequest", "propertyChange"] }] }); }
1153
1852
  }
1154
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
1853
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
1155
1854
  type: Component,
1156
1855
  args: [{ selector: 'dc-lesson-editor', standalone: true, imports: [
1157
- FormsModule,
1856
+ ButtonModule,
1158
1857
  CKEditorModule,
1159
1858
  CropperComponentModal,
1160
- ButtonModule,
1859
+ DCLessonRendererComponent,
1860
+ FormsModule,
1161
1861
  InputTextModule,
1162
1862
  SplitterModule,
1163
- DCLessonRendererComponent,
1164
- LangDescTranslationPipe,
1165
- FlagLanguagePipe,
1166
1863
  TooltipModule,
1167
- SpeedDialModule,
1168
- ], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <div class=\"header-cover\" [style.background-image]=\"cover\"></div>\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</div>\n\n<br />\n\n<div>\n <div>\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"saveLesson()\" />\n\n <p-button label=\"Previsualizar\" severity=\"secondary\" (click)=\"togleRender()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"importFromNotion()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"improveNotionWithAI()\" />\n </div>\n\n <div>\n <input pInputText style=\"width: 100%\" [(ngModel)]=\"lesson.title\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <input pInputText style=\"width: 100%\" [(ngModel)]=\"lesson.description\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n </div>\n\n <div style=\"display: flex; align-items: center\">\n <br />\n <br />\n <input pInputText style=\"flex: auto\" [(ngModel)]=\"lesson.prompt\" type=\"text\" placeholder=\"Prompt\" />\n <p-button severity=\"primary\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\"> Generar </p-button>\n </div>\n\n <div>\n <label class=\"checkbox-container\">\n <input type=\"checkbox\" [(ngModel)]=\"lesson.isPublished\" title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Lecci\u00F3n publicada\n </label>\n <br />\n\n <input pInputText [(ngModel)]=\"lesson.level\" type=\"number\" placeholder=\"Level\" />\n </div>\n\n <div>\n {{ lesson.baseLang | flagEmoji }} -> {{ lesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de {{ lesson.baseLang | langDesc : 'es' }} que aprenden\n {{ lesson.targetLang | langDesc : 'es' }}\n </div>\n </div>\n </div>\n\n <hr />\n <span>Componentes: </span>\n <div style=\"display: flex; gap: 10px\">\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.Selector)\" pTooltip=\"Agrega un selector con multiples opciones\">\n Selector\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.Speaker)\" pTooltip=\"Para que una palabra o frase sea reproducible\">\n Speaker\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.TextWriter)\" pTooltip=\"Escribe una respuesta en un cuadro de texto\">\n Text\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\" pTooltip=\"Muestra la informaci\u00F3n de un verbo\"> Text </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\" pTooltip=\"Muestra la informaci\u00F3n de un verbo\"> Verb </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.WordSummary)\" pTooltip=\"Muestra la informaci\u00F3n de una palabra\">\n Palabra\n </p-button>\n <p-button severity=\"info\" (click)=\"openComponentBuilder(lessonComponentEnum.TranslationSwitcher)\" pTooltip=\"Muestra el texto pero al pica cambia de idioma\">\n Traducci\u00F3n\n </p-button>\n </div>\n\n <hr />\n\n <p-splitter [style]=\"{ height: '80vh' }\" styleClass=\"mb-8\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n (keydown.control.space)=\"togleRender($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [(ngModel)]=\"lesson.textCoded\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n @if (isRendered) {\n <dc-lesson-renderer class=\"text-editor\" [lesson]=\"lesson\" [test]=\"true\"></dc-lesson-renderer>\n }\n </ng-template>\n </p-splitter>\n\n <div class=\"float-button\">\n <p-speeddial [model]=\"items\" [radius]=\"120\" type=\"quarter-circle\" direction=\"up-left\" />\n\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</div>\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.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;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.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"] }]
1169
- }], ctorParameters: () => [{ type: LessonsAbstractService, decorators: [{
1170
- type: Inject,
1171
- args: [LESSONS_TOKEN]
1172
- }] }, { type: i5$1.ToastAlertsAbstractService, decorators: [{
1173
- type: Inject,
1174
- args: [TOAST_ALERTS_TOKEN]
1175
- }] }, { type: NotionAbstractService, decorators: [{
1176
- type: Inject,
1177
- args: [NOTION_SERVICE_TOKEN]
1178
- }] }, { type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: i2.DialogService }], propDecorators: { target: [{
1864
+ // Removed SpeedDialModule
1865
+ DCLessonComponentAdderComponent, // Add the component adder here
1866
+ DCLessonMetadataEditorComponent, // Add the metadata editor here
1867
+ JsonPipe,
1868
+ ], providers: [LessonNotionService], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <div class=\"header-cover\" [style.background-image]=\"coverBackground()\"></div>\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</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (generateAIRequest)=\"generateByAI()\"\n (propertyChange)=\"updateLessonProperty($event.property, $event.value)\">\n</dc-lesson-metadata-editor>\n\n<hr />\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>ID: {{ comp.id }} - Tipo: {{ comp.component }}<button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button></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' }\" styleClass=\"mb-8\">\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()\" [test]=\"true\"></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", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.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;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.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"] }]
1869
+ }], ctorParameters: () => [], propDecorators: { target: [{
1179
1870
  type: ViewChild,
1180
1871
  args: ['target', { read: ViewContainerRef }]
1181
1872
  }], dhtml: [{
@@ -1188,10 +1879,10 @@ class LessonDynamicComponent {
1188
1879
  constructor() {
1189
1880
  this.settings = {};
1190
1881
  }
1191
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: LessonDynamicComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1192
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: LessonDynamicComponent, isStandalone: true, selector: "app-lesson-component", inputs: { settings: "settings" }, ngImport: i0, template: '<div>no template</div>', isInline: true }); }
1882
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonDynamicComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1883
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: LessonDynamicComponent, isStandalone: true, selector: "app-lesson-component", inputs: { settings: "settings" }, ngImport: i0, template: '<div>no template</div>', isInline: true }); }
1193
1884
  }
1194
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: LessonDynamicComponent, decorators: [{
1885
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonDynamicComponent, decorators: [{
1195
1886
  type: Component,
1196
1887
  args: [{
1197
1888
  selector: 'app-lesson-component',