@dataclouder/ngx-lessons 0.0.30 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/dataclouder-ngx-lessons.mjs +1169 -449
- package/fesm2022/dataclouder-ngx-lessons.mjs.map +1 -1
- package/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.d.ts +2 -2
- package/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.d.ts +11 -0
- package/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.d.ts +39 -38
- package/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.d.ts +22 -0
- package/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.d.ts +32 -37
- package/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.d.ts +2 -4
- package/lib/components/lesson-mini-components/components/ComponentBuilder.d.ts +7 -2
- package/lib/components/lesson-mini-components/components/lessons.clases.d.ts +17 -42
- package/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.d.ts +13 -0
- package/lib/components/lesson-mini-components/components/speaker/speaker.component.d.ts +12 -0
- package/lib/services/lesson-ai.service.d.ts +18 -0
- package/lib/services/lesson-notion.service.d.ts +35 -0
- package/lib/services/lesson-utils.service.d.ts +34 -0
- package/package.json +3 -2
- package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.html +0 -35
- package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.scss +0 -107
- package/src/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.ts +0 -82
- package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.css +0 -90
- package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.html +0 -105
- package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.scss +0 -0
- package/src/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.ts +0 -318
- package/src/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.html +0 -27
- package/src/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.scss +0 -3
- package/src/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.ts +0 -271
- package/src/lib/components/dc-lessons/lesson-form/lesson-form.component.html +0 -5
- package/src/lib/components/dc-lessons/lesson-form/lesson-form.component.scss +0 -3
- package/src/lib/components/dc-lessons/lesson-form/lesson-form.component.ts +0 -14
- package/src/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.html +0 -30
- package/src/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.scss +0 -17
- package/src/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.ts +0 -170
- package/src/lib/components/dc-lessons/lessons.component.ts +0 -10
- package/src/lib/components/lesson-mini-components/components/ComponentBuilder.ts +0 -74
- package/src/lib/components/lesson-mini-components/components/ComponentWithForm.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/lesson-dynamic.component.ts +0 -13
- package/src/lib/components/lesson-mini-components/components/lessons.clases.ts +0 -254
- package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.html +0 -58
- package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.scss +0 -15
- package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/selector/selector-builder/selector-builder.component.ts +0 -70
- package/src/lib/components/lesson-mini-components/components/selector/selector.component.html +0 -2
- package/src/lib/components/lesson-mini-components/components/selector/selector.component.scss +0 -12
- package/src/lib/components/lesson-mini-components/components/selector/selector.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/selector/selector.component.ts +0 -47
- package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.html +0 -35
- package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.scss +0 -0
- package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.ts +0 -27
- package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.html +0 -7
- package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.scss +0 -3
- package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/speaker/speaker.component.ts +0 -29
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer-buider/text-writer-buider.component.html +0 -24
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer-buider/text-writer-buider.component.scss +0 -15
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer-buider/text-writer-buider.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer-buider/text-writer-buider.component.ts +0 -29
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer.component.html +0 -4
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer.component.scss +0 -8
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/text-writer/text-writer.component.ts +0 -61
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcher.component.css +0 -3
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcher.component.html +0 -9
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcher.component.ts +0 -32
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcherBuilder/translationSwitcherBuilder.component.css +0 -3
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcherBuilder/translationSwitcherBuilder.component.html +0 -28
- package/src/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcherBuilder/translationSwitcherBuilder.component.ts +0 -30
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary-builder/verb-summary-builder.component.html +0 -18
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary-builder/verb-summary-builder.component.scss +0 -3
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary-builder/verb-summary-builder.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary-builder/verb-summary-builder.component.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary.component.html +0 -15
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary.component.scss +0 -27
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary.component.spec.ts +0 -25
- package/src/lib/components/lesson-mini-components/components/verb-summary/verb-summary.component.ts +0 -46
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary-builder/word-summary-builder.component.html +0 -19
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary-builder/word-summary-builder.component.scss +0 -0
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary-builder/word-summary-builder.component.ts +0 -27
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary.component.html +0 -14
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary.component.scss +0 -22
- package/src/lib/components/lesson-mini-components/components/word-summary/word-summary.component.ts +0 -51
- package/src/lib/models/lessons.pipes.ts +0 -38
- package/src/lib/models/models.ts +0 -92
- package/src/lib/models/notion.models.ts +0 -43
- package/src/public-api.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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
17
|
+
import { NgxTtsComponent } from '@dataclouder/ngx-tts';
|
|
18
|
+
import * as i2$3 from 'primeng/paginator';
|
|
17
19
|
import { PaginatorModule } from 'primeng/paginator';
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
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
|
|
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
|
|
33
|
+
import * as i3$1 from '@ckeditor/ckeditor5-angular';
|
|
29
34
|
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
|
|
30
|
-
import * as
|
|
35
|
+
import * as i5$1 from 'primeng/splitter';
|
|
31
36
|
import { SplitterModule } from 'primeng/splitter';
|
|
32
|
-
import * as
|
|
37
|
+
import * as i2$5 from 'primeng/tooltip';
|
|
33
38
|
import { TooltipModule } from 'primeng/tooltip';
|
|
34
|
-
import {
|
|
35
|
-
import
|
|
36
|
-
import
|
|
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
|
|
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
|
|
78
|
-
|
|
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
|
-
|
|
84
|
+
const stringCode = `~${JSON.stringify(code)}~`;
|
|
85
|
+
alert(stringCode);
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
getComponentData() {
|
|
86
88
|
const code = this.getCode();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
const id = nanoid().replace(/-/g, '_');
|
|
90
|
+
code.id = id;
|
|
91
|
+
return { str: `~${JSON.stringify(code)}~`, obj: code };
|
|
92
|
+
}
|
|
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);
|
|
91
98
|
}
|
|
92
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
93
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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.
|
|
117
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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.
|
|
156
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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,
|
|
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.
|
|
207
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
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.
|
|
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,
|
|
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.
|
|
225
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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.
|
|
268
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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.
|
|
288
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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: [
|
|
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,14 +315,75 @@ 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.
|
|
312
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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 }); }
|
|
320
|
+
}
|
|
321
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslationSwitcherComponent, decorators: [{
|
|
322
|
+
type: Component,
|
|
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"] }]
|
|
324
|
+
}], propDecorators: { config: [{
|
|
325
|
+
type: Input
|
|
326
|
+
}] } });
|
|
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"] }] }); }
|
|
313
379
|
}
|
|
314
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerComponent, decorators: [{
|
|
315
381
|
type: Component,
|
|
316
|
-
args: [{ selector: 'app-
|
|
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"] }]
|
|
317
383
|
}], propDecorators: { config: [{
|
|
318
384
|
type: Input
|
|
385
|
+
}], tts: [{
|
|
386
|
+
type: Input
|
|
319
387
|
}] } });
|
|
320
388
|
|
|
321
389
|
var LessonComponentEnum;
|
|
@@ -329,7 +397,7 @@ var LessonComponentEnum;
|
|
|
329
397
|
})(LessonComponentEnum || (LessonComponentEnum = {}));
|
|
330
398
|
const LessonComponentBuilders = {
|
|
331
399
|
[LessonComponentEnum.Selector]: SelectorBuilderComponent,
|
|
332
|
-
|
|
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
|
-
|
|
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.
|
|
398
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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.
|
|
429
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
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.
|
|
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',
|
|
@@ -466,7 +544,7 @@ var EventCard;
|
|
|
466
544
|
})(EventCard || (EventCard = {}));
|
|
467
545
|
class DcLessonCardComponent {
|
|
468
546
|
constructor() {
|
|
469
|
-
this.
|
|
547
|
+
this.showOptions = true;
|
|
470
548
|
this.onAction = new EventEmitter();
|
|
471
549
|
this.coverUrl = 'assets/background/default-background.webp';
|
|
472
550
|
this.eventType = EventCard;
|
|
@@ -515,15 +593,15 @@ class DcLessonCardComponent {
|
|
|
515
593
|
break;
|
|
516
594
|
}
|
|
517
595
|
}
|
|
518
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
519
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
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"] }] }); }
|
|
520
598
|
}
|
|
521
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
599
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, decorators: [{
|
|
522
600
|
type: Component,
|
|
523
|
-
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [
|
|
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"] }]
|
|
524
602
|
}], propDecorators: { lesson: [{
|
|
525
603
|
type: Input
|
|
526
|
-
}],
|
|
604
|
+
}], showOptions: [{
|
|
527
605
|
type: Input
|
|
528
606
|
}], onAction: [{
|
|
529
607
|
type: Output
|
|
@@ -549,10 +627,10 @@ class DCLessonListComponent extends PaginationBase {
|
|
|
549
627
|
this.route = route;
|
|
550
628
|
this.lessonsService = lessonsService;
|
|
551
629
|
this.toastrService = toastrService;
|
|
552
|
-
this.
|
|
630
|
+
this.showOptions = true;
|
|
553
631
|
this.customFilters = [];
|
|
554
632
|
this.viewType = 'cards';
|
|
555
|
-
|
|
633
|
+
// readonly actions = input<MenuItem[]>(TableViewActions);
|
|
556
634
|
this.columns = tableViewColumns;
|
|
557
635
|
this.cardComponent = null;
|
|
558
636
|
this.cardEventSubs = [];
|
|
@@ -640,19 +718,19 @@ class DCLessonListComponent extends PaginationBase {
|
|
|
640
718
|
this.onAction.emit(actionEvent);
|
|
641
719
|
}
|
|
642
720
|
}
|
|
643
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
644
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
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"] }] }); }
|
|
645
723
|
}
|
|
646
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
724
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, decorators: [{
|
|
647
725
|
type: Component,
|
|
648
|
-
args: [{ selector: 'dc-lesson-list', standalone: true, imports: [
|
|
649
|
-
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$
|
|
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: [{
|
|
650
728
|
type: Inject,
|
|
651
729
|
args: [LESSONS_TOKEN]
|
|
652
|
-
}] }, { type:
|
|
730
|
+
}] }, { type: i4$1.ToastAlertsAbstractService, decorators: [{
|
|
653
731
|
type: Inject,
|
|
654
732
|
args: [TOAST_ALERTS_TOKEN]
|
|
655
|
-
}] }], propDecorators: {
|
|
733
|
+
}] }], propDecorators: { showOptions: [{
|
|
656
734
|
type: Input
|
|
657
735
|
}], customCardComponent: [{
|
|
658
736
|
type: Input
|
|
@@ -660,24 +738,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
660
738
|
type: Input
|
|
661
739
|
}], viewType: [{
|
|
662
740
|
type: Input
|
|
663
|
-
}], actions: [{
|
|
664
|
-
type: Input
|
|
665
741
|
}], outlets: [{
|
|
666
742
|
type: ViewChildren,
|
|
667
743
|
args: ['outlet']
|
|
668
744
|
}] } });
|
|
669
745
|
|
|
670
746
|
class DCLessonFormComponent {
|
|
671
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
672
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
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 }] }); }
|
|
673
749
|
}
|
|
674
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
750
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonFormComponent, decorators: [{
|
|
675
751
|
type: Component,
|
|
676
|
-
args: [{ selector: 'dc-lesson-form', standalone: true, imports: [
|
|
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"] }]
|
|
677
753
|
}] });
|
|
678
754
|
|
|
679
|
-
//
|
|
680
|
-
const
|
|
755
|
+
// Re-define or import constants if they are not exported from the component file
|
|
756
|
+
const DEFAULT_LESSON_AGENT_CARD = {
|
|
681
757
|
conversationSettings: {
|
|
682
758
|
conversationType: ConversationType.General,
|
|
683
759
|
textEngine: TextEngines.SimpleText,
|
|
@@ -693,105 +769,270 @@ const DefaultLessonAgentCard = {
|
|
|
693
769
|
},
|
|
694
770
|
model: { provider: 'google' },
|
|
695
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
|
+
|
|
696
846
|
class DCLessonRendererComponent {
|
|
697
|
-
constructor(
|
|
698
|
-
|
|
699
|
-
this.
|
|
700
|
-
this.
|
|
701
|
-
this.
|
|
702
|
-
|
|
703
|
-
this.
|
|
704
|
-
this.
|
|
705
|
-
this.
|
|
706
|
-
this.
|
|
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 ---
|
|
707
866
|
this.components = {};
|
|
708
|
-
this.mainForm = new
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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
|
+
});
|
|
717
909
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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 = '';
|
|
724
920
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
//
|
|
734
|
-
|
|
735
|
-
//
|
|
736
|
-
|
|
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) {
|
|
737
937
|
const r1 = new RegExp('~(.+?)~', 'g');
|
|
738
938
|
let count = 0;
|
|
739
|
-
|
|
740
|
-
const
|
|
939
|
+
const createdComponents = {};
|
|
940
|
+
const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
|
|
741
941
|
const componentName = `dynamicComp${count}`;
|
|
742
942
|
count++;
|
|
743
|
-
const componentRef = this.
|
|
943
|
+
const componentRef = this._createComponentReferenceWithJson(jsonCoded, lessonData);
|
|
744
944
|
if (!componentRef) {
|
|
745
|
-
|
|
945
|
+
console.error(`Failed to create component for: ${jsonCoded}`);
|
|
946
|
+
return '<!-- component creation failed -->'; // Placeholder in HTML
|
|
746
947
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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.`);
|
|
752
973
|
}
|
|
753
|
-
return `<span id="${componentName}"></span>`;
|
|
754
974
|
});
|
|
755
|
-
this.dynamicLesson.nativeElement.innerHTML = lessonHtml;
|
|
756
|
-
for (let compName of Object.keys(this.components)) {
|
|
757
|
-
// Interpolar componentes dentro del lugar correspondiente
|
|
758
|
-
const elementRef = document.getElementById(compName);
|
|
759
|
-
this.addComponentToNode(this.components[compName], elementRef);
|
|
760
|
-
}
|
|
761
975
|
}
|
|
762
|
-
|
|
976
|
+
_addComponentToNode(componentRef, nodeDOM) {
|
|
763
977
|
this.renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
|
|
764
978
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
//
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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
|
|
777
1015
|
}
|
|
778
|
-
const componentRef = this.viewContainerRef.createComponent(LessonClass);
|
|
779
|
-
componentRef.instance.config = componentConfig || lessonCodedConfig;
|
|
780
|
-
return componentRef;
|
|
781
1016
|
}
|
|
1017
|
+
// --- Evaluation Logic ---
|
|
782
1018
|
async evaluateForms() {
|
|
1019
|
+
this.mainForm.markAllAsTouched(); // Mark all controls for validation feedback
|
|
783
1020
|
if (!this.mainForm.valid) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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' });
|
|
788
1028
|
return;
|
|
789
1029
|
}
|
|
790
1030
|
const rates = { correct: 0, incorrect: 0, score: 0 };
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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') {
|
|
795
1036
|
try {
|
|
796
1037
|
const result = instance.evaluate();
|
|
797
1038
|
if (result) {
|
|
@@ -802,350 +1043,829 @@ class DCLessonRendererComponent {
|
|
|
802
1043
|
}
|
|
803
1044
|
}
|
|
804
1045
|
catch (err) {
|
|
805
|
-
console.error('
|
|
1046
|
+
console.error('Error during evaluation for component:', controlName, instance, err);
|
|
1047
|
+
rates.incorrect++; // Count errors as incorrect
|
|
806
1048
|
}
|
|
807
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;
|
|
808
1056
|
}
|
|
809
|
-
|
|
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' });
|
|
810
1064
|
return;
|
|
811
1065
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
//
|
|
816
|
-
//
|
|
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
|
+
// }
|
|
817
1080
|
if (status === 'passed') {
|
|
818
|
-
this.toastrService.success({ subtitle:
|
|
1081
|
+
this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
|
|
819
1082
|
}
|
|
820
1083
|
else {
|
|
821
|
-
this.toastrService.warn({ subtitle:
|
|
1084
|
+
this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
|
|
822
1085
|
}
|
|
823
1086
|
}
|
|
1087
|
+
// --- AI Chat Logic ---
|
|
824
1088
|
async startAI() {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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' });
|
|
842
1113
|
}
|
|
843
|
-
console.log('lessonText', lessonText);
|
|
844
|
-
const lessonAgentCard = DefaultLessonAgentCard;
|
|
845
|
-
lessonAgentCard.characterCard.data.scenario = scenario;
|
|
846
|
-
lessonAgentCard.characterCard.data.post_history_instructions += `\n${userInformationPrompt}`;
|
|
847
|
-
this.agentMasterLesson = lessonAgentCard;
|
|
848
|
-
this.evaluatorAgentCard = this.getDefualtLessonEvaluatorAgentCard(lessonText);
|
|
849
|
-
this.chatVisible = true;
|
|
850
|
-
}
|
|
851
|
-
getDefualtLessonEvaluatorAgentCard(lessonText) {
|
|
852
|
-
return {
|
|
853
|
-
expectedResponseType: `interface EvalResult {
|
|
854
|
-
score: number; // Score of the user's response 0 to 3
|
|
855
|
-
feedback: string; // Feedback of the user's understanding of the conversation
|
|
856
|
-
}`,
|
|
857
|
-
messages: [],
|
|
858
|
-
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
859
|
-
sources: [lessonText],
|
|
860
|
-
task: `User is reading a taking a lesson, now their are having a conversation,
|
|
861
|
-
you have to evaluate the current conversation, and give a feedback of the user understanding of the lesson,
|
|
862
|
-
this is the lesson: ${lessonText}`,
|
|
863
|
-
};
|
|
864
1114
|
}
|
|
865
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
866
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
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"] }] }); }
|
|
867
1117
|
}
|
|
868
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1118
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
|
|
869
1119
|
type: Component,
|
|
870
|
-
args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [
|
|
871
|
-
}], ctorParameters: () => [
|
|
872
|
-
type: Inject,
|
|
873
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
874
|
-
}] }, { type: LessonsAbstractService, decorators: [{
|
|
875
|
-
type: Inject,
|
|
876
|
-
args: [LESSONS_TOKEN]
|
|
877
|
-
}] }, { type: i2$4.AgentCardsAbstractService, decorators: [{
|
|
878
|
-
type: Inject,
|
|
879
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
880
|
-
}] }, { type: i2$4.DCConversationPromptBuilderService }], propDecorators: { lesson: [{
|
|
881
|
-
type: Input
|
|
882
|
-
}], test: [{
|
|
883
|
-
type: Input
|
|
884
|
-
}], 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: [{
|
|
885
1122
|
type: ViewChild,
|
|
886
1123
|
args: ['dynamicLesson', { static: true }]
|
|
887
1124
|
}] } });
|
|
888
1125
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
this
|
|
893
|
-
this
|
|
894
|
-
|
|
895
|
-
this.
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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;
|
|
913
1154
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
},
|
|
932
|
-
{
|
|
933
|
-
tooltipOptions: {
|
|
934
|
-
tooltipLabel: 'Entrada de texto: Escribe una respuesta en un cuadro de texto',
|
|
935
|
-
tooltipPosition: 'bottom',
|
|
936
|
-
},
|
|
937
|
-
icon: 'pi pi-pencil',
|
|
938
|
-
command: () => this.openComponentBuilder('textWriter'),
|
|
939
|
-
},
|
|
940
|
-
{
|
|
941
|
-
tooltipOptions: { tooltipLabel: 'Verbo: Para ver datos de un verbo', tooltipPosition: 'bottom' },
|
|
942
|
-
icon: 'pi pi-eye',
|
|
943
|
-
command: () => this.openComponentBuilder('verbSummary'),
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
tooltipOptions: { tooltipLabel: 'Palabra: Para ver datos de una palabra', tooltipPosition: 'bottom' },
|
|
947
|
-
icon: 'pi pi-file-word',
|
|
948
|
-
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,
|
|
949
1172
|
},
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
this.
|
|
955
|
-
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;
|
|
956
1179
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
this
|
|
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;
|
|
1185
|
+
}
|
|
1186
|
+
finally {
|
|
1187
|
+
this.isLoading.set(false);
|
|
961
1188
|
}
|
|
962
|
-
this.togleRender();
|
|
963
|
-
this.updateCover();
|
|
964
1189
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
+
}
|
|
969
1213
|
}
|
|
970
1214
|
else {
|
|
971
|
-
|
|
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
|
+
}
|
|
972
1230
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
this.
|
|
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);
|
|
981
1251
|
}
|
|
982
|
-
tag.input.nativeElement.value = '';
|
|
983
1252
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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;
|
|
987
1264
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
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).' });
|
|
993
1274
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
this.
|
|
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);
|
|
998
1282
|
}
|
|
999
|
-
this.togleRender();
|
|
1000
|
-
return lesson;
|
|
1001
1283
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
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) {
|
|
1004
1303
|
return;
|
|
1005
1304
|
}
|
|
1006
|
-
//
|
|
1007
|
-
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1012
|
-
//
|
|
1013
|
-
//
|
|
1014
|
-
//
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
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
|
+
};
|
|
1021
1449
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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;
|
|
1029
1468
|
}
|
|
1469
|
+
// Services
|
|
1470
|
+
#dialogService;
|
|
1471
|
+
#toastService;
|
|
1472
|
+
// Moved logic from DCLessonEditorComponent
|
|
1030
1473
|
openComponentBuilder(type) {
|
|
1031
|
-
|
|
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
|
+
});
|
|
1032
1492
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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.coverImageUrl = computed(() => {
|
|
1595
|
+
const currentLesson = this.lesson();
|
|
1596
|
+
const coverImage = currentLesson?.media?.images?.find((img) => img.type === 'cover');
|
|
1597
|
+
return coverImage?.url || '/assets/images/default_banner.webp';
|
|
1598
|
+
});
|
|
1599
|
+
// Computed signal to get dynamic components as an array for easier iteration in the template
|
|
1600
|
+
this.dynamicComponentsArray = computed(() => {
|
|
1601
|
+
const currentLesson = this.lesson();
|
|
1602
|
+
if (currentLesson?.dynamicComponents) {
|
|
1603
|
+
return Object.values(currentLesson.dynamicComponents);
|
|
1604
|
+
}
|
|
1605
|
+
return []; // Return empty array if no lesson or no dynamic components
|
|
1606
|
+
});
|
|
1607
|
+
// States
|
|
1608
|
+
this.components = {}; // Current Dynamic components
|
|
1609
|
+
this.editor = BalloonEditor;
|
|
1610
|
+
this.lessonComponentEnum = LessonComponentEnum;
|
|
1611
|
+
this.coverStorageSettings = {
|
|
1612
|
+
path: 'lessons/covers',
|
|
1613
|
+
fileName: 'cover',
|
|
1614
|
+
cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
|
|
1615
|
+
};
|
|
1616
|
+
// Effect to fetch lesson data when ID changes
|
|
1617
|
+
effect(async () => {
|
|
1618
|
+
const id = this.lessonId();
|
|
1619
|
+
console.log('Lesson ID Signal:', id);
|
|
1620
|
+
if (id) {
|
|
1621
|
+
this.isLoadingLesson.set(true); // Start loading
|
|
1622
|
+
try {
|
|
1623
|
+
const fetchedLesson = await this.#lessonService.getLesson(id);
|
|
1624
|
+
if (fetchedLesson) {
|
|
1625
|
+
this.lesson.set(fetchedLesson);
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
this.lesson.set(undefined); // Reset if not found
|
|
1629
|
+
this.#toastService.warn({ title: 'No se encontró la lección', subtitle: 'Quizá el id es incorrecto' });
|
|
1630
|
+
// Optional: Navigate away or show a specific "not found" state
|
|
1631
|
+
// this.#router.navigate(['/path/to/lessons']);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
catch (error) {
|
|
1635
|
+
console.error('Error fetching lesson:', error);
|
|
1636
|
+
this.lesson.set(undefined); // Reset on error
|
|
1637
|
+
this.#toastService.error({ title: 'Error al cargar la lección', subtitle: 'Intenta de nuevo más tarde' });
|
|
1638
|
+
}
|
|
1639
|
+
finally {
|
|
1640
|
+
this.isLoadingLesson.set(false); // Stop loading
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
// Handle case for new lesson (ID is null/undefined)
|
|
1645
|
+
this.lesson.set({ textCoded: `<h1>Nueva lección </h1> <p> Texto aquí</p>`, tags: [] }); // Set default new lesson structure
|
|
1646
|
+
this.isLoadingLesson.set(false); // Ensure loading is off
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Updates a specific property on the lesson signal.
|
|
1652
|
+
* Used for ngModelChange events to simulate two-way binding with signals.
|
|
1653
|
+
* @param property The key of the ILesson property to update.
|
|
1654
|
+
* @param value The new value for the property.
|
|
1655
|
+
*/
|
|
1656
|
+
updateLessonProperty(property, value) {
|
|
1657
|
+
this.lesson.update((currentLesson) => {
|
|
1658
|
+
if (!currentLesson)
|
|
1659
|
+
return undefined;
|
|
1660
|
+
return { ...currentLesson, [property]: value };
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
onTagRemove(tag) {
|
|
1664
|
+
this.lesson.update((currentLesson) => {
|
|
1665
|
+
if (!currentLesson)
|
|
1666
|
+
return undefined;
|
|
1667
|
+
const updatedTags = currentLesson.tags.filter((text) => text !== tag.text);
|
|
1668
|
+
return { ...currentLesson, tags: updatedTags };
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
onTagAdd(tag) {
|
|
1672
|
+
if (tag.value) {
|
|
1673
|
+
this.lesson.update((currentLesson) => {
|
|
1674
|
+
if (!currentLesson)
|
|
1675
|
+
return undefined;
|
|
1676
|
+
// Avoid duplicate tags if necessary
|
|
1677
|
+
if (currentLesson.tags.includes(tag.value)) {
|
|
1678
|
+
return currentLesson;
|
|
1679
|
+
}
|
|
1680
|
+
const updatedTags = [...currentLesson.tags, tag.value];
|
|
1681
|
+
return { ...currentLesson, tags: updatedTags };
|
|
1682
|
+
});
|
|
1039
1683
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1684
|
+
tag.input.nativeElement.value = ''; // Clear input
|
|
1685
|
+
}
|
|
1686
|
+
async saveLesson(event) {
|
|
1687
|
+
event?.preventDefault();
|
|
1688
|
+
const currentLesson = this.lesson();
|
|
1689
|
+
if (!currentLesson) {
|
|
1690
|
+
this.#toastService.error({ title: 'Error', subtitle: 'No hay datos de lección para guardar' });
|
|
1691
|
+
return undefined;
|
|
1692
|
+
}
|
|
1693
|
+
// Clean orphaned components before saving
|
|
1694
|
+
const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLesson);
|
|
1695
|
+
// TODO: Implement optimization for saving only changed data.
|
|
1696
|
+
// This requires comparing lessonToSave with the initially fetched state.
|
|
1697
|
+
this.isLoadingLesson.set(true); // Indicate saving
|
|
1698
|
+
try {
|
|
1699
|
+
// Use the cleaned lesson object for saving
|
|
1700
|
+
const savedLesson = await this.#lessonService.postLesson(lessonToSave);
|
|
1701
|
+
const currentId = this.lessonId();
|
|
1702
|
+
if (!currentId) {
|
|
1703
|
+
// It was a new lesson, now it has an ID. Navigate.
|
|
1704
|
+
this.#toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
|
|
1705
|
+
// The effect should automatically fetch the lesson again after navigation due to paramMap change.
|
|
1706
|
+
this.#router.navigate(['../', savedLesson.id], { relativeTo: this.#activatedRoute });
|
|
1707
|
+
}
|
|
1708
|
+
else {
|
|
1709
|
+
// It was an existing lesson, update the signal with the potentially updated data from the backend.
|
|
1710
|
+
this.lesson.set(savedLesson);
|
|
1711
|
+
this.#toastService.info({ title: 'Se guardaron los cambios en la lección', subtitle: 'Guardado' });
|
|
1712
|
+
// Call the service method for validation
|
|
1713
|
+
this.#lessonUtilsService.validateAudios(this.lesson());
|
|
1714
|
+
}
|
|
1715
|
+
return savedLesson;
|
|
1716
|
+
}
|
|
1717
|
+
catch (error) {
|
|
1718
|
+
// Type error
|
|
1719
|
+
console.error('Error saving lesson:', error);
|
|
1720
|
+
this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudieron guardar los cambios' });
|
|
1721
|
+
return undefined;
|
|
1722
|
+
}
|
|
1723
|
+
finally {
|
|
1724
|
+
this.isLoadingLesson.set(false); // Finish saving indication
|
|
1725
|
+
}
|
|
1726
|
+
} // Add missing closing brace for saveLesson
|
|
1727
|
+
// Removed openComponentBuilder method
|
|
1728
|
+
/**
|
|
1729
|
+
* Handles the event emitted when a component is added via the adder component.
|
|
1730
|
+
* @param result The configuration data returned from the component builder dialog. Expected format: { obj: LessonComponentConfiguration }
|
|
1731
|
+
*/
|
|
1732
|
+
onComponentAdded(result) {
|
|
1733
|
+
debugger;
|
|
1734
|
+
// Check if result and result.obj.id exist
|
|
1735
|
+
const newComponent = result?.obj;
|
|
1736
|
+
if (newComponent?.id) {
|
|
1737
|
+
console.log('Component builder closed, result received in editor:', newComponent);
|
|
1738
|
+
// // Transform LessonComponentConfiguration to DynamicContentComponent
|
|
1739
|
+
// const dynamicComponentToAdd: DynamicContentComponent = {
|
|
1740
|
+
// id: componentConfig.id,
|
|
1741
|
+
// component: componentConfig.component,
|
|
1742
|
+
// inputs: {
|
|
1743
|
+
// config: componentConfig, // Pass the original config object as an input named 'config'
|
|
1744
|
+
// // Add other potential inputs if needed based on component type later
|
|
1745
|
+
// },
|
|
1746
|
+
// };
|
|
1747
|
+
// Update the lesson signal, adding the transformed component to the dynamicComponents object
|
|
1748
|
+
this.lesson.update((currentLesson) => {
|
|
1749
|
+
if (!currentLesson)
|
|
1750
|
+
return undefined;
|
|
1751
|
+
// Ensure dynamicComponents object exists, initialize if not
|
|
1752
|
+
const currentDynamicComponents = currentLesson.dynamicComponents || {};
|
|
1753
|
+
// Create the updated dynamicComponents object
|
|
1754
|
+
const updatedDynamicComponents = {
|
|
1755
|
+
...currentDynamicComponents,
|
|
1756
|
+
[newComponent.id]: newComponent, // Use component's id as the key
|
|
1757
|
+
};
|
|
1758
|
+
// Return the updated lesson state
|
|
1759
|
+
return { ...currentLesson, dynamicComponents: updatedDynamicComponents };
|
|
1760
|
+
});
|
|
1761
|
+
// Optionally save the lesson after adding the component
|
|
1762
|
+
// this.saveLesson();
|
|
1045
1763
|
}
|
|
1046
|
-
this.updateCover();
|
|
1047
|
-
this.saveLesson();
|
|
1048
1764
|
}
|
|
1049
1765
|
openCropper() {
|
|
1050
|
-
|
|
1766
|
+
// Correctly define openCropper
|
|
1767
|
+
this.isCropperVisible.set(true);
|
|
1051
1768
|
}
|
|
1769
|
+
// isLoadingLesson signal is used directly
|
|
1052
1770
|
async generateByAI() {
|
|
1053
|
-
|
|
1771
|
+
const currentId = this.lessonId();
|
|
1772
|
+
if (!currentId) {
|
|
1773
|
+
this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
this.isLoadingLesson.set(true);
|
|
1054
1777
|
try {
|
|
1055
|
-
|
|
1056
|
-
await this.
|
|
1057
|
-
|
|
1778
|
+
// Ensure latest changes are saved before generating
|
|
1779
|
+
const savedLesson = await this.saveLesson();
|
|
1780
|
+
if (!savedLesson) {
|
|
1781
|
+
// Handle save error - toast is shown in saveLesson
|
|
1782
|
+
throw new Error('Failed to save before AI generation');
|
|
1783
|
+
}
|
|
1784
|
+
// Call the service method
|
|
1785
|
+
const updatedLesson = await this.#lessonUtilsService.generateByAI(currentId);
|
|
1786
|
+
if (updatedLesson) {
|
|
1787
|
+
this.lesson.set(updatedLesson); // Update the signal with AI changes from service
|
|
1788
|
+
// Toast success is handled by the service
|
|
1789
|
+
}
|
|
1790
|
+
else {
|
|
1791
|
+
// Toast error is handled by the service
|
|
1792
|
+
throw new Error('AI generation failed or lesson fetch failed after generation.');
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
catch (error) {
|
|
1796
|
+
// Type error
|
|
1797
|
+
console.error('Error during AI generation process in component:', error);
|
|
1798
|
+
// Service handles specific AI error toasts, maybe add a general one here if needed
|
|
1799
|
+
// this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
|
|
1058
1800
|
}
|
|
1059
1801
|
finally {
|
|
1060
|
-
this.isLoadingLesson
|
|
1802
|
+
this.isLoadingLesson.set(false); // Stop loading
|
|
1061
1803
|
}
|
|
1062
1804
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1805
|
+
/**
|
|
1806
|
+
* Handles the image upload event, updates the lesson signal via the service, and saves.
|
|
1807
|
+
* @param event The image upload event data.
|
|
1808
|
+
*/
|
|
1066
1809
|
async onImageUploaded(event) {
|
|
1067
|
-
|
|
1068
|
-
this.
|
|
1069
|
-
|
|
1810
|
+
// Call the service to update the signal
|
|
1811
|
+
this.#lessonUtilsService.uploadCover(this.lesson, event);
|
|
1812
|
+
// The coverBackground computed signal will update automatically.
|
|
1813
|
+
// Save the lesson after the signal has been updated
|
|
1814
|
+
await this.saveLesson();
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Imports lesson content from Notion using the LessonNotionService.
|
|
1818
|
+
*/
|
|
1070
1819
|
async importFromNotion() {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1820
|
+
// Use the service's loading state or manage locally
|
|
1821
|
+
this.isLoadingLesson.set(true);
|
|
1822
|
+
try {
|
|
1823
|
+
const newContent = await this.#lessonNotionService.importAndLinkLessonFromNotion(this.lesson(), this.lessonId());
|
|
1824
|
+
if (newContent !== null) {
|
|
1825
|
+
// Update the lesson signal's textCoded property
|
|
1826
|
+
this.updateLessonProperty('textCoded', newContent);
|
|
1827
|
+
// Toast success is handled within the service now
|
|
1076
1828
|
}
|
|
1077
|
-
|
|
1829
|
+
// If newContent is null, the service handled errors/toasts
|
|
1078
1830
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
}
|
|
1084
|
-
notionPageId = this.extractNotionPageId(response);
|
|
1085
|
-
if (!notionPageId)
|
|
1086
|
-
return;
|
|
1087
|
-
this.linkWithNotion(notionPageId);
|
|
1088
|
-
}
|
|
1089
|
-
this.toastService.info({ title: 'Importando lección...', subtitle: 'Espera unos segundos' });
|
|
1090
|
-
const md = await this.notionService.getPageInSpecificFormat(notionPageId, NotionExportType.HTML);
|
|
1091
|
-
console.log(md);
|
|
1092
|
-
this.lesson.textCoded = md.content;
|
|
1093
|
-
this.togleRender();
|
|
1094
|
-
this.toastService.success({ title: 'Listo', subtitle: 'La lección se importó correctamente' });
|
|
1095
|
-
}
|
|
1096
|
-
extractNotionPageId(url) {
|
|
1097
|
-
const notionIdRegex = /[a-f0-9]{32}(?=\?|$)/;
|
|
1098
|
-
const match = url.match(notionIdRegex);
|
|
1099
|
-
const notionId = match ? match[0] : null;
|
|
1100
|
-
if (!notionId) {
|
|
1101
|
-
this.toastService.error({
|
|
1102
|
-
title: 'URL inválido',
|
|
1103
|
-
subtitle: 'Por favor ingresa una URL válida de Notion',
|
|
1104
|
-
});
|
|
1831
|
+
finally {
|
|
1832
|
+
// Ensure loading state is reset regardless of service outcome
|
|
1833
|
+
// If observing service state: this.isLoadingLesson.set(this.#lessonNotionService.isLoading());
|
|
1834
|
+
this.isLoadingLesson.set(false); // Keep local loading for now
|
|
1105
1835
|
}
|
|
1106
|
-
return notionId;
|
|
1107
|
-
}
|
|
1108
|
-
async linkWithNotion(notionPageId) {
|
|
1109
|
-
const extra = this.lesson.extras || {};
|
|
1110
|
-
extra.notionPageId = notionPageId;
|
|
1111
|
-
await this.lessonService.postLesson({ ...this.lesson, extras: extra });
|
|
1112
|
-
this.toastService.success({ title: 'Listo', subtitle: 'Se enlazó la lección con Notion' });
|
|
1113
1836
|
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Calls the LessonNotionService to improve the lesson using AI based on Notion content.
|
|
1839
|
+
*/
|
|
1114
1840
|
async improveNotionWithAI() {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
static { this.ɵ
|
|
1122
|
-
|
|
1841
|
+
await this.#lessonNotionService.improveLessonWithNotionAI(this.lesson());
|
|
1842
|
+
}
|
|
1843
|
+
showComponentDetails(data) {
|
|
1844
|
+
alert('showComponentDetails' + JSON.stringify(data));
|
|
1845
|
+
}
|
|
1846
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1847
|
+
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 <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</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:
|
|
1848
|
+
// Removed SpeedDialModule
|
|
1849
|
+
DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "component", type: // Add the component adder here
|
|
1850
|
+
DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "generateAIRequest", "propertyChange"] }] }); }
|
|
1123
1851
|
}
|
|
1124
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1852
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
|
|
1125
1853
|
type: Component,
|
|
1126
1854
|
args: [{ selector: 'dc-lesson-editor', standalone: true, imports: [
|
|
1127
|
-
|
|
1855
|
+
ButtonModule,
|
|
1128
1856
|
CKEditorModule,
|
|
1129
1857
|
CropperComponentModal,
|
|
1130
|
-
|
|
1858
|
+
DCLessonRendererComponent,
|
|
1859
|
+
FormsModule,
|
|
1131
1860
|
InputTextModule,
|
|
1132
1861
|
SplitterModule,
|
|
1133
|
-
DCLessonRendererComponent,
|
|
1134
|
-
LangDescTranslationPipe,
|
|
1135
|
-
FlagLanguagePipe,
|
|
1136
1862
|
TooltipModule,
|
|
1137
|
-
SpeedDialModule
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
type: Inject,
|
|
1144
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
1145
|
-
}] }, { type: NotionAbstractService, decorators: [{
|
|
1146
|
-
type: Inject,
|
|
1147
|
-
args: [NOTION_SERVICE_TOKEN]
|
|
1148
|
-
}] }, { type: i1$1.ActivatedRoute }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: i2.DialogService }], propDecorators: { target: [{
|
|
1863
|
+
// Removed SpeedDialModule
|
|
1864
|
+
DCLessonComponentAdderComponent, // Add the component adder here
|
|
1865
|
+
DCLessonMetadataEditorComponent, // Add the metadata editor here
|
|
1866
|
+
JsonPipe,
|
|
1867
|
+
], providers: [LessonNotionService], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</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"] }]
|
|
1868
|
+
}], ctorParameters: () => [], propDecorators: { target: [{
|
|
1149
1869
|
type: ViewChild,
|
|
1150
1870
|
args: ['target', { read: ViewContainerRef }]
|
|
1151
1871
|
}], dhtml: [{
|
|
@@ -1158,10 +1878,10 @@ class LessonDynamicComponent {
|
|
|
1158
1878
|
constructor() {
|
|
1159
1879
|
this.settings = {};
|
|
1160
1880
|
}
|
|
1161
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1162
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
1881
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonDynamicComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1882
|
+
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 }); }
|
|
1163
1883
|
}
|
|
1164
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1884
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LessonDynamicComponent, decorators: [{
|
|
1165
1885
|
type: Component,
|
|
1166
1886
|
args: [{
|
|
1167
1887
|
selector: 'app-lesson-component',
|