@dataclouder/ngx-lessons 0.1.3 → 0.1.5
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 +1020 -1008
- package/fesm2022/dataclouder-ngx-lessons.mjs.map +1 -1
- package/index.d.ts +706 -5
- package/package.json +1 -1
- package/lib/components/courses/course-detail/course-detail.component.d.ts +0 -5
- package/lib/components/courses/course-form/course-form.component.d.ts +0 -35
- package/lib/components/courses/course-list/course-list.component.d.ts +0 -25
- package/lib/components/courses/courses.component.d.ts +0 -5
- package/lib/components/courses/courses.service.d.ts +0 -14
- package/lib/components/courses/models/courses.model.d.ts +0 -24
- package/lib/components/courses-admin/courses-admin.component.d.ts +0 -8
- package/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.d.ts +0 -25
- package/lib/components/dc-lessons/dc-lesson-component-adder/dc-lesson-component-adder.component.d.ts +0 -11
- package/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.d.ts +0 -67
- package/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.d.ts +0 -43
- package/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.d.ts +0 -51
- package/lib/components/dc-lessons/dc-lesson-renderer/eval-agents-skills.d.ts +0 -1
- package/lib/components/dc-lessons/lesson-form/lesson-form.component.d.ts +0 -5
- package/lib/components/dc-lessons/lesson-list/dc-lesson-list.component.d.ts +0 -36
- package/lib/components/dynamic-components/dynamic-components-builder.service.d.ts +0 -8
- package/lib/components/dynamic-components/dynamic-components.service.d.ts +0 -30
- package/lib/components/dynamic-components/selector/selector-builder/selector-builder.component.d.ts +0 -16
- package/lib/components/dynamic-components/selector/selector.component.d.ts +0 -12
- package/lib/components/lesson-mini-components/components/ComponentBuilder.d.ts +0 -33
- package/lib/components/lesson-mini-components/components/ComponentWithForm.d.ts +0 -12
- package/lib/components/lesson-mini-components/components/lesson-dynamic.component.d.ts +0 -7
- package/lib/components/lesson-mini-components/components/lessons.clases.d.ts +0 -159
- package/lib/components/lesson-mini-components/components/speaker/speaker-builder/speaker-builder.component.d.ts +0 -14
- package/lib/components/lesson-mini-components/components/speaker/speaker.component.d.ts +0 -12
- package/lib/components/lesson-mini-components/components/text-writer/text-writer-buider/text-writer-buider.component.d.ts +0 -7
- package/lib/components/lesson-mini-components/components/text-writer/text-writer.component.d.ts +0 -16
- package/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcher.component.d.ts +0 -11
- package/lib/components/lesson-mini-components/components/translationSwitcher/translationSwitcherBuilder/translationSwitcherBuilder.component.d.ts +0 -7
- package/lib/models/lessons.pipes.d.ts +0 -28
- package/lib/models/notion.models.d.ts +0 -41
- package/lib/models/simple-agents.d.ts +0 -2
- package/lib/services/courses.service.d.ts +0 -7
- package/lib/services/default-lessons.service.d.ts +0 -20
- package/lib/services/lesson-ai.service.d.ts +0 -32
- package/lib/services/lesson-notion.service.d.ts +0 -35
- package/lib/services/lesson-utils.service.d.ts +0 -56
- package/public-api.d.ts +0 -27
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Pipe, InjectionToken, inject, Input, Component, ChangeDetectionStrategy, signal, EventEmitter, Output,
|
|
3
|
-
import * as i1$2 from 'primeng/paginator';
|
|
4
|
-
import { PaginatorModule } from 'primeng/paginator';
|
|
2
|
+
import { Pipe, InjectionToken, inject, Input, Component, ChangeDetectionStrategy, signal, EventEmitter, Output, Injectable, effect, ViewChildren, input, viewChild, Renderer2, ViewContainerRef, computed, ChangeDetectorRef, output } from '@angular/core';
|
|
5
3
|
import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule, SlicePipe } from '@angular/common';
|
|
6
|
-
import * as i1$
|
|
7
|
-
import { RouterModule, ActivatedRoute,
|
|
8
|
-
import {
|
|
4
|
+
import * as i1$4 from '@angular/router';
|
|
5
|
+
import { RouterModule, ActivatedRoute, RouterOutlet, RouterLink } from '@angular/router';
|
|
6
|
+
import { EntityCommunicationService, EntityBaseListComponent, DCFilterBarComponent, QuickTableComponent, TOAST_ALERTS_TOKEN, EModelQuality, UiStateService, LoadingBarService, LangDescTranslation, FormUtilsService, EntityBaseFormComponent, PromptService, DcExtensionsViewerComponent, DcLearnableViewerComponent, DcAuditableViewerComponent, DcReactionsViewerComponent, DcManageableFormComponent, DcLearnableFormComponent, HttpCoreService, PaginationBase, getSupportedLanguageOptions } from '@dataclouder/ngx-core';
|
|
9
7
|
import * as i1 from '@angular/forms';
|
|
10
8
|
import { FormBuilder, FormControl, FormArray, FormsModule, ReactiveFormsModule, UntypedFormControl, FormGroup, Validators } from '@angular/forms';
|
|
11
9
|
import * as i1$1 from 'primeng/button';
|
|
@@ -14,49 +12,55 @@ import * as i3 from 'primeng/inputtext';
|
|
|
14
12
|
import { InputTextModule } from 'primeng/inputtext';
|
|
15
13
|
import { DynamicDialogRef, DialogService } from 'primeng/dynamicdialog';
|
|
16
14
|
import { nanoid } from 'nanoid';
|
|
17
|
-
import { DropdownModule } from 'primeng/dropdown';
|
|
18
|
-
import { NgxTtsComponent, getRandomQuickVoice } from '@dataclouder/ngx-tts';
|
|
19
|
-
import * as i4 from 'primeng/message';
|
|
20
|
-
import { MessageModule } from 'primeng/message';
|
|
21
15
|
import * as i2 from 'primeng/select';
|
|
22
16
|
import { SelectModule } from 'primeng/select';
|
|
17
|
+
import { TTSPlayground, getRandomQuickVoice, NgxVertexService, ChatRoleVertex } from '@dataclouder/ngx-vertex';
|
|
18
|
+
import * as i4 from 'primeng/message';
|
|
19
|
+
import { MessageModule } from 'primeng/message';
|
|
23
20
|
import { PopoverModule } from 'primeng/popover';
|
|
24
21
|
import * as i4$1 from 'primeng/tag';
|
|
25
22
|
import { TagModule } from 'primeng/tag';
|
|
26
23
|
import * as i2$1 from 'primeng/speeddial';
|
|
27
24
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
28
|
-
import * as
|
|
25
|
+
import * as i1$2 from 'primeng/card';
|
|
29
26
|
import { CardModule } from 'primeng/card';
|
|
30
|
-
import {
|
|
31
|
-
import
|
|
27
|
+
import { UserService } from '@dataclouder/ngx-users';
|
|
28
|
+
import * as i1$3 from 'primeng/paginator';
|
|
29
|
+
import { PaginatorModule } from 'primeng/paginator';
|
|
32
30
|
import BalloonEditor from '@ckeditor/ckeditor5-build-balloon-block';
|
|
33
|
-
import * as i3$
|
|
31
|
+
import * as i3$1 from '@ckeditor/ckeditor5-angular';
|
|
34
32
|
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
|
|
35
|
-
import * as i5$1 from 'primeng/
|
|
33
|
+
import * as i5$1 from 'primeng/selectbutton';
|
|
34
|
+
import { SelectButtonModule } from 'primeng/selectbutton';
|
|
35
|
+
import * as i6$1 from 'primeng/splitter';
|
|
36
36
|
import { SplitterModule } from 'primeng/splitter';
|
|
37
|
-
import * as
|
|
37
|
+
import * as i7 from 'primeng/tooltip';
|
|
38
38
|
import { TooltipModule } from 'primeng/tooltip';
|
|
39
|
-
import { ResolutionType, AspectType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
|
|
40
|
-
import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, EvalResultStringDefinition, ConversationEvents, ChatEventType, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
39
|
+
import { ResolutionType, AspectType, AssetsLoaderComponent, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
|
|
40
|
+
import { TextEngines, ConversationType, USER_DATA_EXCHANGE, SystemPromptType, ChatRole, EvalResultStringDefinition, EDoActionType, ConditionOperator, ConditionType, ConversationEvents, ChatEventType, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
41
41
|
import * as i2$2 from 'primeng/drawer';
|
|
42
42
|
import { DrawerModule } from 'primeng/drawer';
|
|
43
|
-
import
|
|
43
|
+
import { MarkdownComponent, MarkdownService } from 'ngx-markdown';
|
|
44
44
|
import { DialogModule } from 'primeng/dialog';
|
|
45
45
|
import TurndownService from 'turndown';
|
|
46
46
|
import { marked } from 'marked';
|
|
47
47
|
import * as i5 from 'primeng/inputgroup';
|
|
48
48
|
import { InputGroupModule } from 'primeng/inputgroup';
|
|
49
|
-
import * as i6
|
|
49
|
+
import * as i6 from 'primeng/divider';
|
|
50
50
|
import { DividerModule } from 'primeng/divider';
|
|
51
|
-
import { NgxVertexService, ChatRoleVertex } from '@dataclouder/ngx-vertex';
|
|
52
51
|
import * as i2$3 from 'primeng/api';
|
|
52
|
+
import { MessageService } from 'primeng/api';
|
|
53
|
+
import { TableModule } from 'primeng/table';
|
|
54
|
+
import * as i3$2 from 'primeng/panel';
|
|
55
|
+
import { PanelModule } from 'primeng/panel';
|
|
56
|
+
import * as i5$2 from 'primeng/progressspinner';
|
|
57
|
+
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
|
58
|
+
import { ToastModule } from 'primeng/toast';
|
|
53
59
|
import * as i3$3 from 'primeng/textarea';
|
|
54
60
|
import { TextareaModule } from 'primeng/textarea';
|
|
55
61
|
import { ChipModule } from 'primeng/chip';
|
|
56
62
|
import { FormlyModule } from '@ngx-formly/core';
|
|
57
|
-
import { TableModule } from 'primeng/table';
|
|
58
63
|
|
|
59
|
-
// import { LangCodeDescription, LangCodeDescriptionEs } from '../components/lesson-mini-components/components/lessons.clases';
|
|
60
64
|
// This pipe should not be here in the lessons, refactor when is posible and remove LangCodeDescription
|
|
61
65
|
const LangCodeDescription = {
|
|
62
66
|
es: 'Spanish',
|
|
@@ -74,25 +78,6 @@ const LangCodeDescriptionEs = {
|
|
|
74
78
|
fr: 'Frances',
|
|
75
79
|
ja: 'Japonés',
|
|
76
80
|
};
|
|
77
|
-
class LangDescTranslationPipe {
|
|
78
|
-
transform(value, lang) {
|
|
79
|
-
if (lang === 'es') {
|
|
80
|
-
return LangCodeDescriptionEs[value];
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
return LangCodeDescription[value];
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
87
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, isStandalone: true, name: "langDesc" }); }
|
|
88
|
-
}
|
|
89
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: LangDescTranslationPipe, decorators: [{
|
|
90
|
-
type: Pipe,
|
|
91
|
-
args: [{
|
|
92
|
-
name: 'langDesc',
|
|
93
|
-
standalone: true,
|
|
94
|
-
}]
|
|
95
|
-
}] });
|
|
96
81
|
class FlagLanguagePipe {
|
|
97
82
|
transform(lang) {
|
|
98
83
|
if (lang === 'en') {
|
|
@@ -114,10 +99,10 @@ class FlagLanguagePipe {
|
|
|
114
99
|
return '';
|
|
115
100
|
}
|
|
116
101
|
}
|
|
117
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
118
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
102
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: FlagLanguagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
103
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.6", ngImport: i0, type: FlagLanguagePipe, isStandalone: true, name: "flagEmoji" }); }
|
|
119
104
|
}
|
|
120
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
105
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: FlagLanguagePipe, decorators: [{
|
|
121
106
|
type: Pipe,
|
|
122
107
|
args: [{
|
|
123
108
|
name: 'flagEmoji',
|
|
@@ -289,10 +274,10 @@ class ComponentBuilder {
|
|
|
289
274
|
await navigator.clipboard.writeText(data.str);
|
|
290
275
|
this.ref.close(data);
|
|
291
276
|
}
|
|
292
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
293
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
277
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ComponentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
278
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: ComponentBuilder, isStandalone: true, selector: "app-component-builder", inputs: { inputs: "inputs", id: "id" }, ngImport: i0, template: '<div>no template</div>', isInline: true }); }
|
|
294
279
|
}
|
|
295
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
280
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ComponentBuilder, decorators: [{
|
|
296
281
|
type: Component,
|
|
297
282
|
args: [{
|
|
298
283
|
selector: 'app-component-builder',
|
|
@@ -309,10 +294,10 @@ class TextWriterBuiderComponent extends ComponentBuilder {
|
|
|
309
294
|
super(...arguments);
|
|
310
295
|
this.componentName = 'TextWriter';
|
|
311
296
|
}
|
|
312
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
313
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
297
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TextWriterBuiderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
298
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }] }); }
|
|
314
299
|
}
|
|
315
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
300
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TextWriterBuiderComponent, decorators: [{
|
|
316
301
|
type: Component,
|
|
317
302
|
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"] }]
|
|
318
303
|
}] });
|
|
@@ -330,10 +315,10 @@ class ComponentWithForm {
|
|
|
330
315
|
validate() {
|
|
331
316
|
// TODO: generic method to evaluate
|
|
332
317
|
}
|
|
333
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
334
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
318
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ComponentWithForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
319
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: ComponentWithForm, isStandalone: true, selector: "app-component-form", ngImport: i0, template: '<div>no template</div>', isInline: true }); }
|
|
335
320
|
}
|
|
336
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
321
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: ComponentWithForm, decorators: [{
|
|
337
322
|
type: Component,
|
|
338
323
|
args: [{
|
|
339
324
|
selector: 'app-component-form',
|
|
@@ -376,10 +361,10 @@ class TextWriterComponent extends ComponentWithForm {
|
|
|
376
361
|
}
|
|
377
362
|
return true;
|
|
378
363
|
}
|
|
379
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
380
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
364
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TextWriterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
365
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }] }); }
|
|
381
366
|
}
|
|
382
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TextWriterComponent, decorators: [{
|
|
383
368
|
type: Component,
|
|
384
369
|
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"] }]
|
|
385
370
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
@@ -391,10 +376,10 @@ class TranslationSwitcherBuilderComponent extends ComponentBuilder {
|
|
|
391
376
|
super(...arguments);
|
|
392
377
|
this.componentName = 'TranslationSwitcher';
|
|
393
378
|
}
|
|
394
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
395
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
379
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TranslationSwitcherBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
380
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
396
381
|
}
|
|
397
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TranslationSwitcherBuilderComponent, decorators: [{
|
|
398
383
|
type: Component,
|
|
399
384
|
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"] }]
|
|
400
385
|
}] });
|
|
@@ -415,10 +400,10 @@ class TranslationSwitcherComponent {
|
|
|
415
400
|
this.visibleText = this.config.settings.text;
|
|
416
401
|
}
|
|
417
402
|
}
|
|
418
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
419
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
403
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TranslationSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
404
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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 }); }
|
|
420
405
|
}
|
|
421
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
406
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: TranslationSwitcherComponent, decorators: [{
|
|
422
407
|
type: Component,
|
|
423
408
|
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"] }]
|
|
424
409
|
}], propDecorators: { config: [{
|
|
@@ -429,7 +414,7 @@ class SpeakerBuilderComponent extends ComponentBuilder {
|
|
|
429
414
|
constructor() {
|
|
430
415
|
super(...arguments);
|
|
431
416
|
this.componentName = 'Speaker';
|
|
432
|
-
this.tts = signal(undefined);
|
|
417
|
+
this.tts = signal(undefined, ...(ngDevMode ? [{ debugName: "tts" }] : []));
|
|
433
418
|
}
|
|
434
419
|
handleTtsGenerated(event) {
|
|
435
420
|
console.log('TTS generated:', event);
|
|
@@ -450,16 +435,15 @@ class SpeakerBuilderComponent extends ComponentBuilder {
|
|
|
450
435
|
};
|
|
451
436
|
return code;
|
|
452
437
|
}
|
|
453
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
454
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
438
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SpeakerBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
439
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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 <tts-playground (ttsGenerated)=\"handleTtsGenerated($event)\"></tts-playground>\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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: TTSPlayground, selector: "tts-playground", inputs: ["path"], outputs: ["ttsGenerated"] }] }); }
|
|
455
440
|
}
|
|
456
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SpeakerBuilderComponent, decorators: [{
|
|
457
442
|
type: Component,
|
|
458
|
-
args: [{ selector: 'app-speaker-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, InputTextModule, ButtonModule,
|
|
443
|
+
args: [{ selector: 'app-speaker-builder', standalone: true, imports: [FormsModule, ReactiveFormsModule, InputTextModule, ButtonModule, SelectModule, TTSPlayground], template: "<div>\r\n <div>\r\n <h5>Constructor de Speaker</h5>\r\n </div>\r\n\r\n <tts-playground (ttsGenerated)=\"handleTtsGenerated($event)\"></tts-playground>\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" }]
|
|
459
444
|
}] });
|
|
460
445
|
|
|
461
446
|
// ❌ can use this until i copy services to this lib
|
|
462
|
-
// import { CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
463
447
|
class SpeakerComponent {
|
|
464
448
|
ngOnInit() {
|
|
465
449
|
// throw new Error('Method not implemented.');
|
|
@@ -469,16 +453,11 @@ class SpeakerComponent {
|
|
|
469
453
|
// ngOnInit(): void {}
|
|
470
454
|
speach() {
|
|
471
455
|
console.log('should speech but will do in next version');
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
// this.speachService.speach(this.config.settings.text);
|
|
476
|
-
// }
|
|
477
|
-
}
|
|
478
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeakerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
479
|
-
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"] }] }); }
|
|
456
|
+
}
|
|
457
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SpeakerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
458
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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"] }] }); }
|
|
480
459
|
}
|
|
481
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
460
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SpeakerComponent, decorators: [{
|
|
482
461
|
type: Component,
|
|
483
462
|
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"] }]
|
|
484
463
|
}], propDecorators: { config: [{
|
|
@@ -515,10 +494,10 @@ class SelectorComponent extends ComponentWithForm {
|
|
|
515
494
|
}
|
|
516
495
|
return true;
|
|
517
496
|
}
|
|
518
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
519
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
497
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
498
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }] }); }
|
|
520
499
|
}
|
|
521
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
500
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SelectorComponent, decorators: [{
|
|
522
501
|
type: Component,
|
|
523
502
|
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"] }]
|
|
524
503
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
@@ -563,10 +542,10 @@ class SelectorBuilderComponent extends ComponentBuilder {
|
|
|
563
542
|
get optionsForm() {
|
|
564
543
|
return this.formGroup.get('options');
|
|
565
544
|
}
|
|
566
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
567
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
545
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SelectorBuilderComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
546
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", 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: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i4.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }] }); }
|
|
568
547
|
}
|
|
569
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
548
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: SelectorBuilderComponent, decorators: [{
|
|
570
549
|
type: Component,
|
|
571
550
|
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"] }]
|
|
572
551
|
}] });
|
|
@@ -603,8 +582,19 @@ function getLessonComponentClass(type) {
|
|
|
603
582
|
return LessonComponents[type];
|
|
604
583
|
}
|
|
605
584
|
const LESSONS_TOKEN = new InjectionToken('Lessons Service');
|
|
606
|
-
class LessonsAbstractService {
|
|
607
|
-
|
|
585
|
+
// export abstract class LessonsAbstractService extends EntityCommunicationService<ILesson> {
|
|
586
|
+
// abstract getLessons(paginator?: any): Promise<any>;
|
|
587
|
+
// abstract getLesson(id: string): Promise<any>;
|
|
588
|
+
// abstract postLesson(lesson: ILesson): Promise<any>;
|
|
589
|
+
// abstract updateLesson(lesson: ILesson): Promise<any>;
|
|
590
|
+
// abstract deleteLesson(id: string): Promise<any>;
|
|
591
|
+
// abstract generateLesson(lesson: ILesson): Promise<any>;
|
|
592
|
+
// abstract postGenerateByAI(id: string): Promise<any>;
|
|
593
|
+
// abstract extractTextFromHtml(html: string): string;
|
|
594
|
+
// abstract postImproveMDWithAI(lessonId: string, markdownText: string): Promise<any>; // Added for AI Markdown improvement
|
|
595
|
+
// abstract saveTakenLesson(lesson: ILessonTaken): Promise<any>;
|
|
596
|
+
// abstract getPrompts(): LessonPrompts;
|
|
597
|
+
// }
|
|
608
598
|
// my-service.provider.ts
|
|
609
599
|
function provideLessonsService(serviceImplementation) {
|
|
610
600
|
return [
|
|
@@ -621,64 +611,35 @@ var EventCard;
|
|
|
621
611
|
EventCard["Delete"] = "delete";
|
|
622
612
|
EventCard["Select"] = "select";
|
|
623
613
|
EventCard["Qr"] = "qr";
|
|
614
|
+
EventCard["Clone"] = "clone";
|
|
624
615
|
})(EventCard || (EventCard = {}));
|
|
625
616
|
class DcLessonCardComponent {
|
|
626
617
|
constructor() {
|
|
618
|
+
this.userService = inject(UserService);
|
|
627
619
|
this.showOptions = true;
|
|
628
620
|
this.cardHeight = '200px';
|
|
629
621
|
this.onAction = new EventEmitter();
|
|
630
622
|
this.coverUrl = 'assets/background/default-background.webp';
|
|
631
623
|
this.eventType = EventCard;
|
|
632
624
|
this.items = [
|
|
633
|
-
{
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
command: () => {
|
|
637
|
-
this.eventCard(EventCard.Edit);
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
label: 'Eliminar',
|
|
642
|
-
icon: 'pi pi-trash',
|
|
643
|
-
command: () => {
|
|
644
|
-
this.eventCard(EventCard.Delete);
|
|
645
|
-
},
|
|
646
|
-
},
|
|
647
|
-
{
|
|
648
|
-
label: 'Tomar lección',
|
|
649
|
-
icon: 'pi pi-play',
|
|
650
|
-
command: () => {
|
|
651
|
-
this.eventCard(EventCard.Select);
|
|
652
|
-
},
|
|
653
|
-
},
|
|
625
|
+
{ label: 'Clonar', icon: 'pi pi-copy', command: () => this.eventCard(EventCard.Clone) },
|
|
626
|
+
{ label: 'Editar', icon: 'pi pi-pencil', command: () => this.eventCard(EventCard.Edit) },
|
|
627
|
+
{ label: 'Eliminar', icon: 'pi pi-trash', command: () => this.eventCard(EventCard.Delete) },
|
|
654
628
|
];
|
|
655
629
|
}
|
|
656
630
|
ngOnInit() {
|
|
657
|
-
this.coverUrl =
|
|
658
|
-
|
|
631
|
+
this.coverUrl = this.lesson?.assets?.banner?.url || this.lesson?.media?.images?.[0]?.url || 'assets/background/default-background.webp';
|
|
632
|
+
console.log(this.lesson);
|
|
659
633
|
}
|
|
660
634
|
eventCard(eventType) {
|
|
661
|
-
|
|
662
|
-
case EventCard.Edit:
|
|
663
|
-
this.onAction.emit({ action: 'edit', item: this.lesson });
|
|
664
|
-
break;
|
|
665
|
-
case EventCard.Delete:
|
|
666
|
-
this.onAction.emit({ action: 'delete', item: this.lesson });
|
|
667
|
-
break;
|
|
668
|
-
case EventCard.Select:
|
|
669
|
-
this.onAction.emit({ action: 'select', item: this.lesson });
|
|
670
|
-
break;
|
|
671
|
-
case EventCard.Qr:
|
|
672
|
-
this.onAction.emit({ action: 'qr', item: this.lesson });
|
|
673
|
-
break;
|
|
674
|
-
}
|
|
635
|
+
this.onAction.emit({ action: eventType, item: this.lesson });
|
|
675
636
|
}
|
|
676
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
677
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
637
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
638
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions", cardHeight: "cardHeight" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n @if (lesson.learnable?.level){\n <p-tag>Nivel {{ lesson.learnable?.level }}</p-tag>\n } @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if ( userService.isAdmin()) {\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.isPublic ? 'P\u00FAblica' : 'No p\u00FAblica'\" [rounded]=\"true\" />\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.status\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "pipe", type: DatePipe, name: "date" }] }); }
|
|
678
639
|
}
|
|
679
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
640
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DcLessonCardComponent, decorators: [{
|
|
680
641
|
type: Component,
|
|
681
|
-
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n
|
|
642
|
+
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdAt | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title || lesson.metadata?.title || 'No title available' }}</h1>\n <p>{{ lesson.description || lesson.metadata?.description || 'No description available' }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n @if (lesson.learnable?.level){\n <p-tag>Nivel {{ lesson.learnable?.level }}</p-tag>\n } @if (lesson.taken?.status == 'passed') {\n <p-tag severity=\"success\" value=\"Tomada\" [rounded]=\"true\" />\n } @if (lesson.taken?.status == 'failed') {\n <p-tag severity=\"danger\" value=\"Fallida\" [rounded]=\"true\" />\n } @if (lesson.taken) {\n <p-tag severity=\"success\" value=\"Tomada \uD83D\uDCD6\" [rounded]=\"true\" />\n } @if ( userService.isAdmin()) {\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.isPublic ? 'P\u00FAblica' : 'No p\u00FAblica'\" [rounded]=\"true\" />\n <p-tag severity=\"contrast\" [value]=\"lesson.manageable?.status\" [rounded]=\"true\" />\n }\n </div>\n\n <div style=\"position: absolute; bottom: 0px; right: 0px\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem;font-weight:900;text-shadow:1px 1px 2px rgba(0,0,0,.7)}.description p{margin-bottom:1rem;flex-grow:1;font-weight:500;text-shadow:1px 1px 2px rgba(0,0,0,.9)}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}\n"] }]
|
|
682
643
|
}], propDecorators: { lesson: [{
|
|
683
644
|
type: Input
|
|
684
645
|
}], showOptions: [{
|
|
@@ -689,145 +650,430 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
689
650
|
type: Output
|
|
690
651
|
}] } });
|
|
691
652
|
|
|
653
|
+
class DefaultLessonsService extends EntityCommunicationService {
|
|
654
|
+
constructor() {
|
|
655
|
+
super('lesson');
|
|
656
|
+
// --- Endpoint Definitions for methods not in EntityCommunicationService ---
|
|
657
|
+
this.endpoints = {
|
|
658
|
+
saveTakenLesson: `api/user/saveLesson`,
|
|
659
|
+
generateLesson: `api/lesson/generate`,
|
|
660
|
+
generateByAI: `api/lesson/generate-ai`,
|
|
661
|
+
improveMDWithAI: `api/lesson/improve-markdown-ai`,
|
|
662
|
+
GenerateBanner: 'api/lesson/generate-banner',
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
// --- Method Implementations from LessonsAbstractService ---
|
|
666
|
+
async getLessons(filters = {}) {
|
|
667
|
+
if (!filters.returnProps) {
|
|
668
|
+
filters.returnProps = {
|
|
669
|
+
level: 1,
|
|
670
|
+
title: 1,
|
|
671
|
+
name: 1,
|
|
672
|
+
description: 1,
|
|
673
|
+
createdDate: 1,
|
|
674
|
+
banner: 1, // deprecated
|
|
675
|
+
metadata: 1, // deprecated
|
|
676
|
+
manageable: 1,
|
|
677
|
+
assets: 1,
|
|
678
|
+
learnable: 1,
|
|
679
|
+
appExtensions: 1,
|
|
680
|
+
media: 1, // deprecated
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
return this.query(filters);
|
|
684
|
+
}
|
|
685
|
+
async getLesson(id) {
|
|
686
|
+
return this.findOne(id);
|
|
687
|
+
}
|
|
688
|
+
async postLesson(lesson) {
|
|
689
|
+
return this.createOrUpdate(lesson);
|
|
690
|
+
}
|
|
691
|
+
async updateLesson(lesson) {
|
|
692
|
+
if (!lesson._id) {
|
|
693
|
+
throw new Error('Lesson ID is required for update.');
|
|
694
|
+
}
|
|
695
|
+
return this.createOrUpdate(lesson);
|
|
696
|
+
}
|
|
697
|
+
async deleteLesson(id) {
|
|
698
|
+
await this.remove(id);
|
|
699
|
+
}
|
|
700
|
+
// --- Methods specific to lessons ---
|
|
701
|
+
saveTakenLesson(lesson) {
|
|
702
|
+
return this.httpService.post(this.endpoints.saveTakenLesson, lesson);
|
|
703
|
+
}
|
|
704
|
+
async generateLesson(lesson) {
|
|
705
|
+
return this.httpService.post(this.endpoints.generateLesson, lesson);
|
|
706
|
+
}
|
|
707
|
+
async postGenerateByAI(id) {
|
|
708
|
+
return this.httpService.post(this.endpoints.generateByAI, { id });
|
|
709
|
+
}
|
|
710
|
+
async postImproveMDWithAI(lessonId, markdownText) {
|
|
711
|
+
return this.httpService.post(this.endpoints.improveMDWithAI, { id: lessonId, markdown: markdownText });
|
|
712
|
+
}
|
|
713
|
+
generateBanner(prompt, lessonId) {
|
|
714
|
+
return this.httpService.post(this.endpoints.GenerateBanner, { prompt, lessonId });
|
|
715
|
+
}
|
|
716
|
+
extractTextFromHtml(html) {
|
|
717
|
+
const r1 = new RegExp('~(.+?)~', 'g');
|
|
718
|
+
const lessonHtml = html.replace(r1, (_matching, jsonCoded) => {
|
|
719
|
+
try {
|
|
720
|
+
const data = JSON.parse(jsonCoded);
|
|
721
|
+
return `<span>${data?.settings?.text || ''}</span>`;
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
console.error('Error parsing JSON in extractTextFromHtml:', jsonCoded, e);
|
|
725
|
+
return '';
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
let text = lessonHtml.replace(/<[^>]*>/g, ' ');
|
|
729
|
+
text = text.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, ' ');
|
|
730
|
+
text = text.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, ' ');
|
|
731
|
+
text = text.replace(/ /g, ' ');
|
|
732
|
+
text = text.replace(/&/g, '&');
|
|
733
|
+
text = text.replace(/</g, '<');
|
|
734
|
+
text = text.replace(/>/g, '>');
|
|
735
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
736
|
+
return text;
|
|
737
|
+
}
|
|
738
|
+
getPrompts() {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DefaultLessonsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
742
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DefaultLessonsService, providedIn: 'root' }); }
|
|
743
|
+
}
|
|
744
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DefaultLessonsService, decorators: [{
|
|
745
|
+
type: Injectable,
|
|
746
|
+
args: [{
|
|
747
|
+
providedIn: 'root',
|
|
748
|
+
}]
|
|
749
|
+
}], ctorParameters: () => [] });
|
|
750
|
+
|
|
692
751
|
const tableViewColumns = [
|
|
693
752
|
{ field: 'media.images[0].url', header: 'Image', type: 'image' },
|
|
694
753
|
{ field: 'title', header: 'Título' },
|
|
695
754
|
{ field: 'description', header: 'Descripción' },
|
|
696
755
|
];
|
|
697
|
-
|
|
698
|
-
{ title: 'select', label: 'Select', icon: 'pi pi-check', severity: 'primary' },
|
|
699
|
-
{ title: 'qr', label: 'QR', icon: 'pi pi-qrcode', severity: 'info' },
|
|
700
|
-
{ title: 'edit', label: 'Edit', icon: 'pi pi-pencil', severity: 'info' },
|
|
701
|
-
{ title: 'delete', label: 'Delete', icon: 'pi pi-trash', severity: 'danger' },
|
|
702
|
-
];
|
|
703
|
-
const returnProperties = { id: 1, title: 1, assets: 1 };
|
|
704
|
-
class DCLessonListComponent extends PaginationBase {
|
|
756
|
+
class DCLessonListComponent extends EntityBaseListComponent {
|
|
705
757
|
constructor() {
|
|
706
|
-
super(
|
|
707
|
-
this.showOptions = true;
|
|
758
|
+
super();
|
|
708
759
|
this.customFilters = [];
|
|
709
|
-
this.viewType = 'cards';
|
|
710
760
|
// Injected Services
|
|
711
|
-
|
|
712
|
-
this.
|
|
713
|
-
this.toastrService = inject(TOAST_ALERTS_TOKEN);
|
|
761
|
+
// I dont rename so use the same in EntityBaseListComponent and Pagination
|
|
762
|
+
this.entityCommunicationService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
714
763
|
this.columns = tableViewColumns;
|
|
715
|
-
|
|
764
|
+
// Private properties
|
|
716
765
|
this.cardEventSubs = [];
|
|
717
|
-
this.
|
|
718
|
-
|
|
766
|
+
this.filterConfig.returnProps = {
|
|
767
|
+
title: 1,
|
|
768
|
+
description: 1,
|
|
769
|
+
media: 1,
|
|
770
|
+
manageable: 1,
|
|
771
|
+
learnable: 1,
|
|
772
|
+
assets: 1,
|
|
773
|
+
_id: 1,
|
|
774
|
+
id: 1,
|
|
775
|
+
};
|
|
776
|
+
effect(() => {
|
|
777
|
+
// When items signal changes, re-subscribe to card events
|
|
778
|
+
this.items(); // a little hack to make the effect run when items change
|
|
779
|
+
setTimeout(() => this.subscribeToCardEvents());
|
|
780
|
+
});
|
|
719
781
|
}
|
|
720
|
-
ngOnInit() {
|
|
721
|
-
this.filterConfig.returnProps = returnProperties;
|
|
782
|
+
async ngOnInit() {
|
|
722
783
|
this.cardComponent = this.customCardComponent || DcLessonCardComponent;
|
|
723
|
-
|
|
724
|
-
this.filterBarOptions = { showCreateButton: this.showOptions, showViewButton: this.showOptions, showActions: this.showOptions };
|
|
725
|
-
this.getPaginatedLessons(this.filterConfig);
|
|
784
|
+
await super.ngOnInit();
|
|
726
785
|
}
|
|
727
|
-
|
|
728
|
-
this.
|
|
729
|
-
this.subscribeToCardEvents();
|
|
730
|
-
}
|
|
731
|
-
subscribeToCardEvents() {
|
|
732
|
-
this.outlets.forEach((outlet) => {
|
|
733
|
-
const instance = outlet.componentInstance;
|
|
734
|
-
this.cardEventSubs.push(instance.onAction.subscribe((lesson) => {
|
|
735
|
-
this.doOrEmitAction(lesson);
|
|
736
|
-
}));
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
clearcardEventSubs() {
|
|
740
|
-
this.cardEventSubs.forEach((sub) => sub.unsubscribe());
|
|
741
|
-
this.cardEventSubs = [];
|
|
786
|
+
ngAfterViewInit() {
|
|
787
|
+
this.outlets.changes.subscribe(() => this.subscribeToCardEvents());
|
|
742
788
|
}
|
|
743
789
|
ngOnDestroy() {
|
|
744
|
-
this.
|
|
790
|
+
this.clearCardEventSubs();
|
|
745
791
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
this.
|
|
749
|
-
this.
|
|
750
|
-
|
|
751
|
-
const lessons = await this.lessonsService.getLessons(paginator);
|
|
752
|
-
this.lessons = lessons['rows'] || lessons;
|
|
753
|
-
this.totalRecords = lessons['count'] || this.lessons.length;
|
|
754
|
-
console.log('lessons', lessons);
|
|
755
|
-
setTimeout(() => {
|
|
756
|
-
this.subscribeDinamicInstantToEvents();
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
finally {
|
|
760
|
-
this.isLoadingLessons = false;
|
|
761
|
-
this.cdr.detectChanges();
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
search(searchText) {
|
|
765
|
-
this.filterConfig['text'] = searchText;
|
|
766
|
-
this.getPaginatedLessons(this.filterConfig);
|
|
792
|
+
getCustomButtons(item) {
|
|
793
|
+
return [
|
|
794
|
+
{ label: 'Select', icon: 'pi pi-check', command: () => this.handleAction({ action: 'select', item }) },
|
|
795
|
+
{ label: 'QR', icon: 'pi pi-qrcode', command: () => this.handleAction({ action: 'qr', item }) },
|
|
796
|
+
];
|
|
767
797
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
this.toastrService.success({ title: 'Adios a la lección', subtitle: 'La lección ha sido eliminada correctamente' });
|
|
798
|
+
handleAction(actionEvent) {
|
|
799
|
+
if (['select', 'qr'].includes(actionEvent.action)) {
|
|
800
|
+
this.onAction.emit(actionEvent);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
super.doAction(actionEvent);
|
|
775
804
|
}
|
|
776
|
-
}
|
|
777
|
-
newLesson() {
|
|
778
|
-
this.onAction.emit({ action: 'new' });
|
|
779
805
|
}
|
|
780
806
|
applyFilterBarEvent(filterEvent) {
|
|
781
|
-
if (filterEvent.action
|
|
782
|
-
this.
|
|
807
|
+
if (filterEvent.action === 'changeView') {
|
|
808
|
+
this.toggleView();
|
|
783
809
|
return;
|
|
784
810
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
this.filterConfig = filterEvent.item;
|
|
788
|
-
this.filterConfig.returnProps = returnProperties;
|
|
789
|
-
this.getPaginatedLessons(this.filterConfig);
|
|
790
|
-
}
|
|
811
|
+
this.filterConfig = { ...this.filterConfig, ...filterEvent.item };
|
|
812
|
+
this.loadData();
|
|
791
813
|
}
|
|
792
|
-
|
|
793
|
-
|
|
814
|
+
subscribeToCardEvents() {
|
|
815
|
+
this.clearCardEventSubs();
|
|
816
|
+
this.outlets.forEach((outlet) => {
|
|
817
|
+
const instance = outlet.componentInstance;
|
|
818
|
+
if (instance?.onAction) {
|
|
819
|
+
this.cardEventSubs.push(instance.onAction.subscribe((event) => {
|
|
820
|
+
this.handleAction(event);
|
|
821
|
+
}));
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
clearCardEventSubs() {
|
|
826
|
+
this.cardEventSubs.forEach((sub) => sub.unsubscribe());
|
|
827
|
+
this.cardEventSubs = [];
|
|
794
828
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
this.
|
|
829
|
+
async loadData() {
|
|
830
|
+
try {
|
|
831
|
+
this.isLoading = true;
|
|
832
|
+
const response = await this.entityCommunicationService.getLessons(this.filterConfig);
|
|
833
|
+
this.items.set(response.rows);
|
|
834
|
+
this.totalRecordsSignal.set(response.count);
|
|
798
835
|
}
|
|
799
|
-
|
|
800
|
-
|
|
836
|
+
catch (error) {
|
|
837
|
+
console.error('Error loading data', error);
|
|
838
|
+
}
|
|
839
|
+
finally {
|
|
840
|
+
this.isLoading = false;
|
|
801
841
|
}
|
|
802
842
|
}
|
|
803
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
804
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
843
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
844
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { customCardComponent: "customCardComponent", customFilters: "customFilters" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n class=\"paginator-container\"\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}.paginator-container{background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }] }); }
|
|
805
845
|
}
|
|
806
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
846
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonListComponent, decorators: [{
|
|
807
847
|
type: Component,
|
|
808
|
-
args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent,
|
|
809
|
-
}], propDecorators: {
|
|
810
|
-
type: Input
|
|
811
|
-
}], customCardComponent: [{
|
|
848
|
+
args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, NgComponentOutlet, QuickTableComponent, PaginatorModule], template: "<dc-filter-bar [options]=\"filterBarOptions\" [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"onNew()\"></dc-filter-bar>\n@if(viewType() === 'table') {\n\n<app-quick-table [tableData]=\"items()\" (onAction)=\"doAction($event)\"></app-quick-table>\n\n} @else {\n<div class=\"lesson-list-container\">\n @if (items()?.length > 0) { @for (lesson of items(); track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: true\n }\">\n </ng-container>\n\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n class=\"paginator-container\"\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [first]=\"first\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}.paginator-container{background:transparent}\n"] }]
|
|
849
|
+
}], ctorParameters: () => [], propDecorators: { customCardComponent: [{
|
|
812
850
|
type: Input
|
|
813
851
|
}], customFilters: [{
|
|
814
852
|
type: Input
|
|
815
|
-
}], viewType: [{
|
|
816
|
-
type: Input
|
|
817
853
|
}], outlets: [{
|
|
818
854
|
type: ViewChildren,
|
|
819
855
|
args: ['outlet']
|
|
820
856
|
}] } });
|
|
821
857
|
|
|
822
858
|
class DCLessonFormComponent {
|
|
823
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
824
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
859
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
860
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", 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 }] }); }
|
|
825
861
|
}
|
|
826
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
862
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonFormComponent, decorators: [{
|
|
827
863
|
type: Component,
|
|
828
864
|
args: [{ selector: 'dc-lesson-form', standalone: true, imports: [ReactiveFormsModule], template: "<div class=\"lesson-form-container\">\n <h2>Lesson Form</h2>\n <p>Esto es dentro del componente de la leccion</p>\n <!-- Form implementation will go here -->\n</div>\n", styles: [".lesson-form-container{padding:1rem}\n"] }]
|
|
829
865
|
}] });
|
|
830
866
|
|
|
867
|
+
// TODO: check LessonComponentBuilders in lessons.clases.ts for origianl implementation
|
|
868
|
+
const DynamicComponentBuilders = {
|
|
869
|
+
[LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
|
|
870
|
+
};
|
|
871
|
+
const DynamicComponents = {
|
|
872
|
+
[LessonComponentEnum.Speaker]: SpeakerComponent,
|
|
873
|
+
};
|
|
874
|
+
class DynamicComponentsService {
|
|
875
|
+
constructor() {
|
|
876
|
+
this._dynamicComponentBuilders = { ...DynamicComponentBuilders };
|
|
877
|
+
this._dynamicComponents = { ...DynamicComponents };
|
|
878
|
+
}
|
|
879
|
+
registerCustomComponent(component, name) {
|
|
880
|
+
// this._dynamicComponentBuilders[name || component.name] = component;
|
|
881
|
+
this._dynamicComponents[name || component.name] = component;
|
|
882
|
+
}
|
|
883
|
+
registerCustomComponentBuilder(component, name) {
|
|
884
|
+
this._dynamicComponentBuilders[name || component.name] = component;
|
|
885
|
+
}
|
|
886
|
+
getDynamicComponentBuilders(type) {
|
|
887
|
+
return this._dynamicComponentBuilders[type];
|
|
888
|
+
}
|
|
889
|
+
getDynamicComponentClass(type) {
|
|
890
|
+
return this._dynamicComponents[type];
|
|
891
|
+
}
|
|
892
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
893
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, providedIn: 'root' }); }
|
|
894
|
+
}
|
|
895
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsService, decorators: [{
|
|
896
|
+
type: Injectable,
|
|
897
|
+
args: [{
|
|
898
|
+
providedIn: 'root',
|
|
899
|
+
}]
|
|
900
|
+
}] });
|
|
901
|
+
|
|
902
|
+
class LessonRendererService {
|
|
903
|
+
constructor() {
|
|
904
|
+
this.dynamicComponentsService = inject(DynamicComponentsService);
|
|
905
|
+
this.toastrService = inject(TOAST_ALERTS_TOKEN);
|
|
906
|
+
this.components = {};
|
|
907
|
+
this.mainForm = new FormGroup({});
|
|
908
|
+
}
|
|
909
|
+
renderLesson(lessonData, viewContainerRef, dynamicLessonElement, renderer) {
|
|
910
|
+
this.clearLessonRendering(dynamicLessonElement);
|
|
911
|
+
console.log('Rendering lesson:', lessonData.id);
|
|
912
|
+
const { htmlContent, components } = this.parseAndCreateComponents(lessonData, viewContainerRef);
|
|
913
|
+
this.components = components;
|
|
914
|
+
this.aggregateFormControls(this.components);
|
|
915
|
+
dynamicLessonElement.innerHTML = htmlContent;
|
|
916
|
+
this.injectComponentsIntoDom(this.components, renderer);
|
|
917
|
+
return this.components;
|
|
918
|
+
}
|
|
919
|
+
clearLessonRendering(dynamicLessonElement) {
|
|
920
|
+
Object.values(this.components).forEach((compRef) => compRef.destroy());
|
|
921
|
+
this.components = {};
|
|
922
|
+
this.mainForm = new FormGroup({});
|
|
923
|
+
if (dynamicLessonElement) {
|
|
924
|
+
dynamicLessonElement.innerHTML = '';
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
parseAndCreateComponents(lessonData, viewContainerRef) {
|
|
928
|
+
const r1 = new RegExp('~(.+?)~', 'g');
|
|
929
|
+
let count = 0;
|
|
930
|
+
const createdComponents = {};
|
|
931
|
+
const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
|
|
932
|
+
const componentName = `dynamicComp${count}`;
|
|
933
|
+
count++;
|
|
934
|
+
const componentRef = this.createComponentReferenceWithJson(jsonCoded, lessonData, viewContainerRef);
|
|
935
|
+
if (!componentRef) {
|
|
936
|
+
console.error(`Failed to create component for: ${jsonCoded}`);
|
|
937
|
+
return '<!-- component creation failed -->';
|
|
938
|
+
}
|
|
939
|
+
createdComponents[componentName] = componentRef;
|
|
940
|
+
return `<span id="${componentName}"></span>`;
|
|
941
|
+
});
|
|
942
|
+
return { htmlContent, components: createdComponents };
|
|
943
|
+
}
|
|
944
|
+
createComponentReferenceWithJson(json, currentLesson, viewContainerRef) {
|
|
945
|
+
try {
|
|
946
|
+
let lessonCodedConfig = JSON.parse(json);
|
|
947
|
+
if (lessonCodedConfig.id && currentLesson?.dynamicComponents[lessonCodedConfig.id]) {
|
|
948
|
+
const foundConfig = currentLesson.dynamicComponents[lessonCodedConfig.id];
|
|
949
|
+
if (foundConfig) {
|
|
950
|
+
lessonCodedConfig = { ...lessonCodedConfig, ...foundConfig };
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const LessonClass = this.dynamicComponentsService.getDynamicComponentClass(lessonCodedConfig.component);
|
|
954
|
+
if (!LessonClass) {
|
|
955
|
+
console.error(`Component class not found for type: ${lessonCodedConfig.component}. JSON: ${json}`);
|
|
956
|
+
return null;
|
|
957
|
+
}
|
|
958
|
+
const componentRef = viewContainerRef.createComponent(LessonClass);
|
|
959
|
+
if (lessonCodedConfig.inputs) {
|
|
960
|
+
for (const key in lessonCodedConfig.inputs) {
|
|
961
|
+
if (lessonCodedConfig.inputs.hasOwnProperty(key)) {
|
|
962
|
+
componentRef.instance[key] = lessonCodedConfig.inputs[key];
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (lessonCodedConfig.settings) {
|
|
967
|
+
componentRef.instance.config = lessonCodedConfig;
|
|
968
|
+
}
|
|
969
|
+
return componentRef;
|
|
970
|
+
}
|
|
971
|
+
catch (error) {
|
|
972
|
+
console.error(`Error processing component JSON: ${json}`, error);
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
aggregateFormControls(components) {
|
|
977
|
+
const newFormControls = {};
|
|
978
|
+
Object.entries(components).forEach(([name, componentRef]) => {
|
|
979
|
+
if (componentRef.instance?.control instanceof FormControl) {
|
|
980
|
+
newFormControls[name] = componentRef.instance.control;
|
|
981
|
+
newFormControls[name].addValidators(Validators.required);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
this.mainForm = new FormGroup(newFormControls);
|
|
985
|
+
}
|
|
986
|
+
injectComponentsIntoDom(components, renderer) {
|
|
987
|
+
Object.entries(components).forEach(([name, componentRef]) => {
|
|
988
|
+
const elementRef = document.getElementById(name);
|
|
989
|
+
if (elementRef) {
|
|
990
|
+
this.addComponentToNode(componentRef, elementRef, renderer);
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
console.warn(`Placeholder element with ID '${name}' not found in the DOM.`);
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
addComponentToNode(componentRef, nodeDOM, renderer) {
|
|
998
|
+
renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
|
|
999
|
+
}
|
|
1000
|
+
evaluateForms() {
|
|
1001
|
+
this.mainForm.markAllAsTouched();
|
|
1002
|
+
if (!this.mainForm.valid) {
|
|
1003
|
+
Object.keys(this.mainForm.controls).forEach((controlName) => {
|
|
1004
|
+
if (this.components[controlName]?.instance?.validate) {
|
|
1005
|
+
this.components[controlName].instance.validate();
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
this.toastrService.warn({ subtitle: 'Por favor completa todos los ejercicios', title: 'Incompleto' });
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
const rates = { correct: 0, incorrect: 0, score: 0 };
|
|
1012
|
+
Object.keys(this.mainForm.controls).forEach((controlName) => {
|
|
1013
|
+
const instance = this.components[controlName]?.instance;
|
|
1014
|
+
if (instance && typeof instance.evaluate === 'function') {
|
|
1015
|
+
try {
|
|
1016
|
+
const result = instance.evaluate();
|
|
1017
|
+
if (result) {
|
|
1018
|
+
rates.correct++;
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
rates.incorrect++;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
catch (err) {
|
|
1025
|
+
console.error('Error during evaluation for component:', controlName, instance, err);
|
|
1026
|
+
rates.incorrect++;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
const totalQuestions = rates.correct + rates.incorrect;
|
|
1031
|
+
rates.score = totalQuestions > 0 ? rates.correct / totalQuestions : 0;
|
|
1032
|
+
const status = rates.score >= 0.7 ? 'passed' : 'failed';
|
|
1033
|
+
if (status === 'passed') {
|
|
1034
|
+
this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
|
|
1038
|
+
}
|
|
1039
|
+
return { rates, takenLesson: null };
|
|
1040
|
+
}
|
|
1041
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1042
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, providedIn: 'root' }); }
|
|
1043
|
+
}
|
|
1044
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonRendererService, decorators: [{
|
|
1045
|
+
type: Injectable,
|
|
1046
|
+
args: [{
|
|
1047
|
+
providedIn: 'root',
|
|
1048
|
+
}]
|
|
1049
|
+
}] });
|
|
1050
|
+
|
|
1051
|
+
const EnglishEvaluationSkill = `
|
|
1052
|
+
You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
|
|
1053
|
+
|
|
1054
|
+
You will be provided with two pieces of information for each evaluation:
|
|
1055
|
+
1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
|
|
1056
|
+
2. **A conversation transcript:** This will be the dialogue between the Student and an Assistant, with each turn clearly labeled (e.g., "Student or User: ...", "Assistant: ...").
|
|
1057
|
+
|
|
1058
|
+
Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
|
|
1059
|
+
|
|
1060
|
+
Evaluate the student's performance based on the following aspects, *always keeping the student's provided level in mind* and adjusting your expectations accordingly specially with beginners give them credits just for trying:
|
|
1061
|
+
|
|
1062
|
+
* **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
|
|
1063
|
+
* **Vocabulary & Usage:** Evaluate the range and appropriateness of words used *relative to the expected level*. (Beginners: focus on using basic words correctly; Advanced: expect varied and idiomatic language).
|
|
1064
|
+
* **Cohesion & Coherence:** Evaluate how well their turns connect and make sense within the conversation *relative to the expected level*. (Beginners: focus on simple, clear responses; Advanced: expect logical flow and detailed contributions).
|
|
1065
|
+
* **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
|
|
1066
|
+
|
|
1067
|
+
Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
|
|
1068
|
+
|
|
1069
|
+
* **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
|
|
1070
|
+
* **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
|
|
1071
|
+
* **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
|
|
1072
|
+
* **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
|
|
1073
|
+
|
|
1074
|
+
Provide detailed, constructive feedback that explains the rating. Point out specific strengths and areas for improvement based on their performance *at their given level*. Use examples from the dialog if helpful. Maintain a supportive, encouraging, and educational tone appropriate for a virtual professor guiding a student at their specific stage.
|
|
1075
|
+
`;
|
|
1076
|
+
|
|
831
1077
|
// Re-define or import constants if they are not exported from the component file
|
|
832
1078
|
const DEFAULT_LESSON_AGENT_CARD = {
|
|
833
1079
|
conversationSettings: {
|
|
@@ -843,7 +1089,6 @@ const DEFAULT_LESSON_AGENT_CARD = {
|
|
|
843
1089
|
post_history_instructions: 'Your reply should be always short, 1 or 2 paragraphs at most, and to the point, and you should ask friendly questions all the time',
|
|
844
1090
|
},
|
|
845
1091
|
},
|
|
846
|
-
model: { provider: 'google' },
|
|
847
1092
|
};
|
|
848
1093
|
const AppRolePlaySkill = `
|
|
849
1094
|
You are an app role play assistant from Polilan App. 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.
|
|
@@ -854,14 +1099,13 @@ You are an engagement assistant, start by greeting the user, asking something ab
|
|
|
854
1099
|
that makes sense for the lesson.
|
|
855
1100
|
`;
|
|
856
1101
|
function getDefaultLessonEvaluatorAgentCard(lessonText) {
|
|
1102
|
+
// TODO: probably i need to build in order to add the sources to the task or system.
|
|
857
1103
|
return {
|
|
858
1104
|
expectedResponseType: `interface EvalResult {
|
|
859
1105
|
score: number; // Score of the user's response 0 to 3
|
|
860
1106
|
feedback: string; // Feedback of the user's understanding of the conversation
|
|
861
1107
|
}`,
|
|
862
|
-
messages: [],
|
|
863
1108
|
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
864
|
-
sources: [lessonText],
|
|
865
1109
|
task: `User is reading a taking a lesson, now their are having a conversation,
|
|
866
1110
|
you have to evaluate the current conversation, and give a feedback of the user understanding of the lesson,
|
|
867
1111
|
this is the lesson: ${lessonText}`,
|
|
@@ -869,8 +1113,7 @@ function getDefaultLessonEvaluatorAgentCard(lessonText) {
|
|
|
869
1113
|
}
|
|
870
1114
|
class LessonAIService {
|
|
871
1115
|
constructor() {
|
|
872
|
-
this.
|
|
873
|
-
// TODO: Inject the application-level UserService
|
|
1116
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
874
1117
|
this.userService = inject(USER_DATA_EXCHANGE);
|
|
875
1118
|
}
|
|
876
1119
|
/**
|
|
@@ -894,7 +1137,7 @@ ${userInformationPrompt}`;
|
|
|
894
1137
|
* @returns An object containing the master agent card and the evaluator agent card.
|
|
895
1138
|
*/
|
|
896
1139
|
async generateAgentCards(lesson) {
|
|
897
|
-
const lessonText = this.
|
|
1140
|
+
const lessonText = this.lessonsService.extractTextFromHtml(lesson.textCoded);
|
|
898
1141
|
const userInformationPrompt = this.userService.getUserDataInformation();
|
|
899
1142
|
const scenario = this._buildScenarioPrompt(lessonText, userInformationPrompt);
|
|
900
1143
|
// Create a deep copy of the default card to avoid modifying the constant
|
|
@@ -912,7 +1155,13 @@ ${userInformationPrompt}`;
|
|
|
912
1155
|
async generateConversationSettingsForLesson(lesson, settings) {
|
|
913
1156
|
// TODO: Consolidate user fetching logic if possible, or ensure consistency
|
|
914
1157
|
const baseLang = this.userService.getUserDataExchange()?.baseLang || 'en';
|
|
915
|
-
|
|
1158
|
+
let lessonText = '';
|
|
1159
|
+
if (lesson.textCoded) {
|
|
1160
|
+
lessonText = this.lessonsService.extractTextFromHtml(lesson.textCoded);
|
|
1161
|
+
}
|
|
1162
|
+
else {
|
|
1163
|
+
lessonText = lesson.markdown;
|
|
1164
|
+
}
|
|
916
1165
|
const userInformationPrompt = this.userService.getUserDataInformation();
|
|
917
1166
|
let scenario = this._buildScenarioPrompt(lessonText, userInformationPrompt);
|
|
918
1167
|
if (settings.additionalPrompt) {
|
|
@@ -921,6 +1170,7 @@ ${userInformationPrompt}`;
|
|
|
921
1170
|
const initialMessage = {
|
|
922
1171
|
role: ChatRole.System,
|
|
923
1172
|
content: scenario,
|
|
1173
|
+
messageId: SystemPromptType.SystemPrompt,
|
|
924
1174
|
};
|
|
925
1175
|
// Use defaults similar to DEFAULT_LESSON_AGENT_CARD but adjust for prompt-based start
|
|
926
1176
|
const conversationSettings = {
|
|
@@ -929,75 +1179,107 @@ ${userInformationPrompt}`;
|
|
|
929
1179
|
autoStart: true,
|
|
930
1180
|
messages: [initialMessage],
|
|
931
1181
|
model: { provider: 'google' },
|
|
932
|
-
voice: getRandomQuickVoice(baseLang || 'en', 'female'),
|
|
1182
|
+
tts: { voice: getRandomQuickVoice(baseLang || 'en', 'female') },
|
|
933
1183
|
};
|
|
934
1184
|
return conversationSettings;
|
|
935
1185
|
}
|
|
936
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
937
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1186
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonAIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1187
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonAIService, providedIn: 'root' }); }
|
|
938
1188
|
}
|
|
939
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonAIService, decorators: [{
|
|
940
1190
|
type: Injectable,
|
|
941
1191
|
args: [{
|
|
942
1192
|
providedIn: 'root', // Or provide appropriately if it's library-specific
|
|
943
1193
|
}]
|
|
944
1194
|
}] });
|
|
945
1195
|
|
|
946
|
-
|
|
947
|
-
You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
|
|
948
|
-
|
|
949
|
-
You will be provided with two pieces of information for each evaluation:
|
|
950
|
-
1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
|
|
951
|
-
2. **A conversation transcript:** This will be the dialogue between the Student and an Assistant, with each turn clearly labeled (e.g., "Student or User: ...", "Assistant: ...").
|
|
952
|
-
|
|
953
|
-
Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
|
|
954
|
-
|
|
955
|
-
Evaluate the student's performance based on the following aspects, *always keeping the student's provided level in mind* and adjusting your expectations accordingly specially with beginners give them credits just for trying:
|
|
956
|
-
|
|
957
|
-
* **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
|
|
958
|
-
* **Vocabulary & Usage:** Evaluate the range and appropriateness of words used *relative to the expected level*. (Beginners: focus on using basic words correctly; Advanced: expect varied and idiomatic language).
|
|
959
|
-
* **Cohesion & Coherence:** Evaluate how well their turns connect and make sense within the conversation *relative to the expected level*. (Beginners: focus on simple, clear responses; Advanced: expect logical flow and detailed contributions).
|
|
960
|
-
* **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
|
|
961
|
-
|
|
962
|
-
Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
|
|
963
|
-
|
|
964
|
-
* **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
|
|
965
|
-
* **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
|
|
966
|
-
* **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
|
|
967
|
-
* **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
|
|
968
|
-
|
|
969
|
-
Provide detailed, constructive feedback that explains the rating. Point out specific strengths and areas for improvement based on their performance *at their given level*. Use examples from the dialog if helpful. Maintain a supportive, encouraging, and educational tone appropriate for a virtual professor guiding a student at their specific stage.
|
|
970
|
-
`;
|
|
971
|
-
|
|
972
|
-
// TODO: check LessonComponentBuilders in lessons.clases.ts for origianl implementation
|
|
973
|
-
const DynamicComponentBuilders = {
|
|
974
|
-
[LessonComponentEnum.Speaker]: SpeakerBuilderComponent,
|
|
975
|
-
};
|
|
976
|
-
const DynamicComponents = {
|
|
977
|
-
[LessonComponentEnum.Speaker]: SpeakerComponent,
|
|
978
|
-
};
|
|
979
|
-
class DynamicComponentsService {
|
|
1196
|
+
class LessonConversationService {
|
|
980
1197
|
constructor() {
|
|
981
|
-
this.
|
|
982
|
-
this.
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
this._dynamicComponents[name || component.name] = component;
|
|
987
|
-
}
|
|
988
|
-
registerCustomComponentBuilder(component, name) {
|
|
989
|
-
this._dynamicComponentBuilders[name || component.name] = component;
|
|
1198
|
+
this.lessonAIService = inject(LessonAIService);
|
|
1199
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
1200
|
+
this.conversationSettings = signal(undefined, ...(ngDevMode ? [{ debugName: "conversationSettings" }] : []));
|
|
1201
|
+
this.conversationFlow = signal(undefined, ...(ngDevMode ? [{ debugName: "conversationFlow" }] : []));
|
|
1202
|
+
this.evalAgentTask = signal(undefined, ...(ngDevMode ? [{ debugName: "evalAgentTask" }] : []));
|
|
990
1203
|
}
|
|
991
|
-
|
|
992
|
-
|
|
1204
|
+
initializeConversationFlow() {
|
|
1205
|
+
this.evalAgentTask.set({
|
|
1206
|
+
systemPrompt: EnglishEvaluationSkill,
|
|
1207
|
+
model: { provider: 'google' },
|
|
1208
|
+
task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
|
|
1209
|
+
expectedResponseType: EvalResultStringDefinition,
|
|
1210
|
+
});
|
|
1211
|
+
this.conversationFlow.set({
|
|
1212
|
+
goal: {
|
|
1213
|
+
enabled: true,
|
|
1214
|
+
task: `User is learning is taking a lesson about languages, evaluate how good are the responses.`,
|
|
1215
|
+
model: { quality: EModelQuality.FAST },
|
|
1216
|
+
},
|
|
1217
|
+
triggerTasks: {
|
|
1218
|
+
[ConversationEvents.OnUserMessage]: {
|
|
1219
|
+
enabled: true,
|
|
1220
|
+
...this.evalAgentTask(),
|
|
1221
|
+
},
|
|
1222
|
+
},
|
|
1223
|
+
challenges: null,
|
|
1224
|
+
dynamicConditions: [
|
|
1225
|
+
{
|
|
1226
|
+
what: ConditionType.Goal,
|
|
1227
|
+
when: ConditionOperator.GreaterThanOrEqual,
|
|
1228
|
+
value: 100,
|
|
1229
|
+
do: [
|
|
1230
|
+
{
|
|
1231
|
+
actionType: EDoActionType.ChangePrompt,
|
|
1232
|
+
systemPromptType: SystemPromptType.SystemPrompt,
|
|
1233
|
+
prompt: 'You were talking with the user, user finish the conversation, in the next message you must try to finish the conversation and say good bye.',
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
actionType: EDoActionType.ChangePrompt,
|
|
1237
|
+
systemPromptType: SystemPromptType.CharacterDescription,
|
|
1238
|
+
prompt: '',
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
actionType: EDoActionType.ChangePrompt,
|
|
1242
|
+
systemPromptType: SystemPromptType.UserInformation,
|
|
1243
|
+
prompt: '',
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
actionType: EDoActionType.ChangePrompt,
|
|
1247
|
+
systemPromptType: SystemPromptType.MessageExamples,
|
|
1248
|
+
prompt: '',
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
actionType: EDoActionType.ChangePrompt,
|
|
1252
|
+
systemPromptType: SystemPromptType.ScenarioDescription,
|
|
1253
|
+
prompt: '',
|
|
1254
|
+
},
|
|
1255
|
+
],
|
|
1256
|
+
},
|
|
1257
|
+
],
|
|
1258
|
+
});
|
|
993
1259
|
}
|
|
994
|
-
|
|
995
|
-
|
|
1260
|
+
async startAI(lesson, settings) {
|
|
1261
|
+
console.log('Requesting agent cards from LessonAIService...');
|
|
1262
|
+
try {
|
|
1263
|
+
const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(lesson, settings);
|
|
1264
|
+
if (conversationSettings) {
|
|
1265
|
+
this.conversationSettings.set(conversationSettings);
|
|
1266
|
+
console.log('Agent cards received and set.');
|
|
1267
|
+
return conversationSettings;
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
console.error('Failed to generate agent cards (service returned null).');
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
catch (error) {
|
|
1275
|
+
console.error('Error generating agent cards:', error);
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
996
1278
|
}
|
|
997
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
998
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1279
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1280
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, providedIn: 'root' }); }
|
|
999
1281
|
}
|
|
1000
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1282
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonConversationService, decorators: [{
|
|
1001
1283
|
type: Injectable,
|
|
1002
1284
|
args: [{
|
|
1003
1285
|
providedIn: 'root',
|
|
@@ -1007,29 +1289,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1007
1289
|
class DCLessonRendererComponent {
|
|
1008
1290
|
constructor() {
|
|
1009
1291
|
// --- Signal Inputs ---
|
|
1010
|
-
this.lessonInput = input(); // Input signal for lesson object
|
|
1011
|
-
this.lessonIdInput = input(); // Input signal for lesson ID
|
|
1012
|
-
this.settings = input();
|
|
1292
|
+
this.lessonInput = input(...(ngDevMode ? [undefined, { debugName: "lessonInput" }] : [])); // Input signal for lesson object
|
|
1293
|
+
this.lessonIdInput = input(...(ngDevMode ? [undefined, { debugName: "lessonIdInput" }] : [])); // Input signal for lesson ID
|
|
1294
|
+
this.settings = input(...(ngDevMode ? [undefined, { debugName: "settings" }] : []));
|
|
1013
1295
|
// --- Outputs ---
|
|
1014
1296
|
this.wordClicked = new EventEmitter(); // New output event
|
|
1297
|
+
// --- View Childs ---
|
|
1298
|
+
this.dynamicLesson = viewChild('dynamicLesson', ...(ngDevMode ? [{ debugName: "dynamicLesson" }] : []));
|
|
1015
1299
|
// --- Services ---
|
|
1016
1300
|
this.renderer = inject(Renderer2);
|
|
1017
1301
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
1018
1302
|
this.toastrService = inject(TOAST_ALERTS_TOKEN);
|
|
1019
|
-
this.
|
|
1020
|
-
this.
|
|
1021
|
-
this.
|
|
1022
|
-
this.
|
|
1303
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
1304
|
+
this.uiStateService = inject(UiStateService);
|
|
1305
|
+
this.lessonRendererService = inject(LessonRendererService);
|
|
1306
|
+
this.lessonConversationService = inject(LessonConversationService);
|
|
1023
1307
|
// --- State Signals ---
|
|
1024
|
-
this.lesson = signal(undefined); // Internal lesson state signal
|
|
1025
|
-
this.chatVisible = signal(false); // Signal for chat visibility
|
|
1026
|
-
this.agentMasterLesson = signal(undefined); // Signal for agent card
|
|
1027
|
-
this.evaluatorAgentCard = signal(undefined); // Signal for evaluator card
|
|
1028
|
-
this.conversationSettings = signal(undefined);
|
|
1029
|
-
this.evalAgentTask = signal(undefined); // Signal for evaluator card
|
|
1030
|
-
this.backgroundTasks = {};
|
|
1308
|
+
this.lesson = signal(undefined, ...(ngDevMode ? [{ debugName: "lesson" }] : [])); // Internal lesson state signal
|
|
1031
1309
|
// --- Computed Signals ---
|
|
1032
|
-
this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url); // Computed signal for imageCover
|
|
1310
|
+
this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url, ...(ngDevMode ? [{ debugName: "imageCover" }] : [])); // Computed signal for imageCover
|
|
1033
1311
|
// --- Properties ---
|
|
1034
1312
|
this.components = {};
|
|
1035
1313
|
this.mainForm = new FormGroup({});
|
|
@@ -1046,7 +1324,7 @@ class DCLessonRendererComponent {
|
|
|
1046
1324
|
console.log(`[Renderer] Effect 1: Fetching lesson ${lessonId}`);
|
|
1047
1325
|
try {
|
|
1048
1326
|
// Consider adding a loading state signal here
|
|
1049
|
-
const fetchedLesson = await this.
|
|
1327
|
+
const fetchedLesson = await this.lessonsService.getLesson(lessonId);
|
|
1050
1328
|
this.lesson.set(fetchedLesson);
|
|
1051
1329
|
console.log('Fetched lesson:', fetchedLesson);
|
|
1052
1330
|
}
|
|
@@ -1066,204 +1344,64 @@ class DCLessonRendererComponent {
|
|
|
1066
1344
|
}, { allowSignalWrites: true }); // Allow signal writes inside effect
|
|
1067
1345
|
// Effect to render the lesson only when textCoded value actually changes
|
|
1068
1346
|
effect(() => {
|
|
1347
|
+
const dynamicLessonEl = this.dynamicLesson();
|
|
1069
1348
|
const currentLesson = this.lesson(); // Read the lesson signal
|
|
1349
|
+
if (!dynamicLessonEl || !currentLesson) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (currentLesson?.format === 'markdown') {
|
|
1353
|
+
// If markdown content exists, we clear any dynamically rendered components
|
|
1354
|
+
// and prevent the textCoded logic from running.
|
|
1355
|
+
if (this.previousTextCoded) {
|
|
1356
|
+
this.lessonRendererService.clearLessonRendering(dynamicLessonEl.nativeElement);
|
|
1357
|
+
this.mainForm = this.lessonRendererService.mainForm;
|
|
1358
|
+
this.previousTextCoded = undefined;
|
|
1359
|
+
}
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1070
1362
|
const newTextCoded = currentLesson?.textCoded; // Get the current textCoded value
|
|
1071
|
-
this.evalAgentTask.set({
|
|
1072
|
-
systemPrompt: EnglishEvaluationSkill,
|
|
1073
|
-
model: { provider: 'google' },
|
|
1074
|
-
task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
|
|
1075
|
-
expectedResponseType: EvalResultStringDefinition,
|
|
1076
|
-
});
|
|
1077
1363
|
// Quick fix to only render on changes textCode not all the lesson
|
|
1078
1364
|
if (newTextCoded !== this.previousTextCoded) {
|
|
1079
1365
|
if (newTextCoded !== undefined && newTextCoded !== null && currentLesson) {
|
|
1080
|
-
this.
|
|
1366
|
+
this.components = this.lessonRendererService.renderLesson(currentLesson, this.viewContainerRef, dynamicLessonEl.nativeElement, this.renderer);
|
|
1367
|
+
this.mainForm = this.lessonRendererService.mainForm;
|
|
1081
1368
|
}
|
|
1082
1369
|
else {
|
|
1083
|
-
this.
|
|
1084
|
-
|
|
1085
|
-
// Update the stored previous value *after* comparison and action
|
|
1086
|
-
this.previousTextCoded = newTextCoded;
|
|
1087
|
-
}
|
|
1088
|
-
else {
|
|
1089
|
-
console.log('[Renderer] textCoded has NOT changed. Skipping render/clear.');
|
|
1090
|
-
}
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
ngOnInit() {
|
|
1094
|
-
this.backgroundTasks = {
|
|
1095
|
-
[ConversationEvents.OnUserMessage]: this.evalAgentTask(),
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
// --- Rendering Logic ---
|
|
1099
|
-
_clearLessonRendering() {
|
|
1100
|
-
// Destroy previously created dynamic components
|
|
1101
|
-
Object.values(this.components).forEach((compRef) => compRef.destroy());
|
|
1102
|
-
this.components = {};
|
|
1103
|
-
// Clear the form
|
|
1104
|
-
this.mainForm = new FormGroup({});
|
|
1105
|
-
// Clear the HTML content
|
|
1106
|
-
if (this.dynamicLesson?.nativeElement) {
|
|
1107
|
-
this.dynamicLesson.nativeElement.innerHTML = '';
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
_renderLesson(lessonData) {
|
|
1111
|
-
this._clearLessonRendering(); // Clear previous state first
|
|
1112
|
-
console.log('Rendering lesson:', lessonData.id);
|
|
1113
|
-
// 1) Parse textCoded, create components, and build HTML structure
|
|
1114
|
-
const { htmlContent, components } = this._parseAndCreateComponents(lessonData);
|
|
1115
|
-
this.components = components;
|
|
1116
|
-
// 2) Aggregate form controls from created components
|
|
1117
|
-
this._aggregateFormControls(this.components);
|
|
1118
|
-
// 3) Set the innerHTML of the target element
|
|
1119
|
-
this.dynamicLesson.nativeElement.innerHTML = htmlContent;
|
|
1120
|
-
// 4) Inject the component views into the DOM
|
|
1121
|
-
this._injectComponentsIntoDom(this.components);
|
|
1122
|
-
}
|
|
1123
|
-
_parseAndCreateComponents(lessonData) {
|
|
1124
|
-
const r1 = new RegExp('~(.+?)~', 'g');
|
|
1125
|
-
let count = 0;
|
|
1126
|
-
const createdComponents = {};
|
|
1127
|
-
const htmlContent = lessonData.textCoded.replace(r1, (_matching, jsonCoded) => {
|
|
1128
|
-
const componentName = `dynamicComp${count}`;
|
|
1129
|
-
count++;
|
|
1130
|
-
const componentRef = this._createComponentReferenceWithJson(jsonCoded, lessonData);
|
|
1131
|
-
if (!componentRef) {
|
|
1132
|
-
console.error(`Failed to create component for: ${jsonCoded}`);
|
|
1133
|
-
return '<!-- component creation failed -->'; // Placeholder in HTML
|
|
1134
|
-
}
|
|
1135
|
-
createdComponents[componentName] = componentRef;
|
|
1136
|
-
return `<span id="${componentName}"></span>`; // Return span placeholder
|
|
1137
|
-
});
|
|
1138
|
-
return { htmlContent, components: createdComponents };
|
|
1139
|
-
}
|
|
1140
|
-
_aggregateFormControls(components) {
|
|
1141
|
-
const newFormControls = {};
|
|
1142
|
-
Object.entries(components).forEach(([name, componentRef]) => {
|
|
1143
|
-
// Check if the instance has a control property that is a FormControl
|
|
1144
|
-
if (componentRef.instance?.control instanceof FormControl) {
|
|
1145
|
-
newFormControls[name] = componentRef.instance.control;
|
|
1146
|
-
// Add required validator (consider making this configurable via component config?)
|
|
1147
|
-
newFormControls[name].addValidators(Validators.required);
|
|
1148
|
-
}
|
|
1149
|
-
});
|
|
1150
|
-
this.mainForm = new FormGroup(newFormControls); // Create the typed FormGroup
|
|
1151
|
-
}
|
|
1152
|
-
_injectComponentsIntoDom(components) {
|
|
1153
|
-
Object.entries(components).forEach(([name, componentRef]) => {
|
|
1154
|
-
const elementRef = document.getElementById(name); // Find the placeholder span
|
|
1155
|
-
if (elementRef) {
|
|
1156
|
-
this._addComponentToNode(componentRef, elementRef);
|
|
1157
|
-
}
|
|
1158
|
-
else {
|
|
1159
|
-
console.warn(`Placeholder element with ID '${name}' not found in the DOM.`);
|
|
1160
|
-
}
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
_addComponentToNode(componentRef, nodeDOM) {
|
|
1164
|
-
this.renderer.appendChild(nodeDOM, componentRef.location.nativeElement);
|
|
1165
|
-
}
|
|
1166
|
-
_createComponentReferenceWithJson(json, currentLesson) {
|
|
1167
|
-
try {
|
|
1168
|
-
let lessonCodedConfig = JSON.parse(json);
|
|
1169
|
-
//
|
|
1170
|
-
// Attempt to find pre-configured component data in the lesson object by ID
|
|
1171
|
-
if (lessonCodedConfig.id && currentLesson?.dynamicComponents[lessonCodedConfig.id]) {
|
|
1172
|
-
const foundConfig = currentLesson.dynamicComponents[lessonCodedConfig.id];
|
|
1173
|
-
if (foundConfig) {
|
|
1174
|
-
// Merge configurations: Start with coded, override/add with found lesson config
|
|
1175
|
-
lessonCodedConfig = { ...lessonCodedConfig, ...foundConfig };
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
const LessonClass = this.dynamicComponentsService.getDynamicComponentClass(lessonCodedConfig.component);
|
|
1179
|
-
if (!LessonClass) {
|
|
1180
|
-
console.error(`Component class not found for type: ${lessonCodedConfig.component}. JSON: ${json}`);
|
|
1181
|
-
return null; // Return null if class doesn't exist
|
|
1182
|
-
}
|
|
1183
|
-
// Create component instance
|
|
1184
|
-
const componentRef = this.viewContainerRef.createComponent(LessonClass);
|
|
1185
|
-
if (lessonCodedConfig.inputs) {
|
|
1186
|
-
for (const key in lessonCodedConfig.inputs) {
|
|
1187
|
-
if (lessonCodedConfig.inputs.hasOwnProperty(key)) {
|
|
1188
|
-
componentRef.instance[key] = lessonCodedConfig.inputs[key];
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
// i think i can improve this to pass only settings not all config, Assign the configuration to the component instance
|
|
1193
|
-
// settings data i defined in form interface, config will be all the component data including id and component type
|
|
1194
|
-
if (lessonCodedConfig.settings) {
|
|
1195
|
-
componentRef.instance.config = lessonCodedConfig;
|
|
1196
|
-
}
|
|
1197
|
-
return componentRef;
|
|
1198
|
-
}
|
|
1199
|
-
catch (error) {
|
|
1200
|
-
console.error(`Error processing component JSON: ${json}`, error);
|
|
1201
|
-
return null; // Return null on JSON parsing or other errors
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
// --- Evaluation Logic ---
|
|
1205
|
-
async evaluateForms() {
|
|
1206
|
-
this.mainForm.markAllAsTouched(); // Mark all controls for validation feedback
|
|
1207
|
-
if (!this.mainForm.valid) {
|
|
1208
|
-
// Trigger validation feedback on individual components visually if needed
|
|
1209
|
-
Object.keys(this.mainForm.controls).forEach((controlName) => {
|
|
1210
|
-
if (this.components[controlName]?.instance?.validate) {
|
|
1211
|
-
this.components[controlName].instance.validate(); // Assuming validate method handles visual feedback
|
|
1212
|
-
}
|
|
1213
|
-
});
|
|
1214
|
-
this.toastrService.warn({ subtitle: 'Por favor completa todos los ejercicios', title: 'Incompleto' });
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
const rates = { correct: 0, incorrect: 0, score: 0 };
|
|
1218
|
-
// Evaluate each component associated with a form control
|
|
1219
|
-
Object.keys(this.mainForm.controls).forEach((controlName) => {
|
|
1220
|
-
const instance = this.components[controlName]?.instance;
|
|
1221
|
-
// Check if the instance has an evaluate method (duck typing is okay here)
|
|
1222
|
-
if (instance && typeof instance.evaluate === 'function') {
|
|
1223
|
-
try {
|
|
1224
|
-
const result = instance.evaluate();
|
|
1225
|
-
if (result) {
|
|
1226
|
-
rates.correct++;
|
|
1227
|
-
}
|
|
1228
|
-
else {
|
|
1229
|
-
rates.incorrect++;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
catch (err) {
|
|
1233
|
-
console.error('Error during evaluation for component:', controlName, instance, err);
|
|
1234
|
-
rates.incorrect++; // Count errors as incorrect
|
|
1370
|
+
this.lessonRendererService.clearLessonRendering(dynamicLessonEl.nativeElement);
|
|
1371
|
+
this.mainForm = this.lessonRendererService.mainForm;
|
|
1235
1372
|
}
|
|
1373
|
+
// Update the stored previous value *after* comparison and action
|
|
1374
|
+
this.previousTextCoded = newTextCoded;
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
console.log('[Renderer] textCoded has NOT changed. Skipping render/clear.');
|
|
1236
1378
|
}
|
|
1237
1379
|
});
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1380
|
+
}
|
|
1381
|
+
ngOnInit() {
|
|
1382
|
+
this.lessonConversationService.initializeConversationFlow();
|
|
1383
|
+
}
|
|
1384
|
+
async evaluateForms() {
|
|
1385
|
+
const result = this.lessonRendererService.evaluateForms();
|
|
1386
|
+
if (!result) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1241
1389
|
const currentLesson = this.lesson();
|
|
1242
1390
|
if (!currentLesson) {
|
|
1243
1391
|
console.error('Cannot save result, lesson data is missing.');
|
|
1244
1392
|
this.toastrService.error({ subtitle: 'Cannot save result, lesson data missing.', title: 'Error' });
|
|
1245
1393
|
return;
|
|
1246
1394
|
}
|
|
1247
|
-
const
|
|
1395
|
+
const status = result.rates.score >= 0.7 ? 'passed' : 'failed';
|
|
1396
|
+
const takenLesson = {
|
|
1397
|
+
id: currentLesson.id,
|
|
1398
|
+
goalCompleted: null,
|
|
1399
|
+
score: result.rates.score,
|
|
1400
|
+
status: status,
|
|
1401
|
+
lastAccess: new Date(),
|
|
1402
|
+
};
|
|
1248
1403
|
console.log('Lesson evaluation result:', takenLesson);
|
|
1249
1404
|
// TODO: Re-implement saving the taken lesson status via lessonService
|
|
1250
|
-
// try {
|
|
1251
|
-
// await this.lessonService.saveTakenLesson(takenLesson);
|
|
1252
|
-
// if (status === 'passed') {
|
|
1253
|
-
// this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Se guardó tu progreso.`, title: '¡Muy bien!' });
|
|
1254
|
-
// } else {
|
|
1255
|
-
// this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
|
|
1256
|
-
// }
|
|
1257
|
-
// } catch (error) {
|
|
1258
|
-
// console.error('Failed to save taken lesson', error);
|
|
1259
|
-
// this.toastrService.error({ subtitle: 'No se pudo guardar tu progreso.', title: 'Error' });
|
|
1260
|
-
// }
|
|
1261
|
-
if (status === 'passed') {
|
|
1262
|
-
this.toastrService.success({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%.`, title: '¡Muy bien!' });
|
|
1263
|
-
}
|
|
1264
|
-
else {
|
|
1265
|
-
this.toastrService.warn({ subtitle: `Calificación: ${Math.round(rates.score * 100)}%. Revisa tus respuestas.`, title: 'Casi lo logras' });
|
|
1266
|
-
}
|
|
1267
1405
|
}
|
|
1268
1406
|
// --- AI Chat Logic ---
|
|
1269
1407
|
async startAI() {
|
|
@@ -1273,35 +1411,27 @@ class DCLessonRendererComponent {
|
|
|
1273
1411
|
this.toastrService.error({ subtitle: 'Lesson data not available.', title: 'Cannot Start Chat' });
|
|
1274
1412
|
return;
|
|
1275
1413
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(currentLesson, this.settings());
|
|
1280
|
-
if (conversationSettings) {
|
|
1281
|
-
this.conversationSettings.set(conversationSettings);
|
|
1282
|
-
this.chatVisible.set(true);
|
|
1283
|
-
console.log('Agent cards received and set.');
|
|
1284
|
-
}
|
|
1285
|
-
else {
|
|
1286
|
-
console.error('Failed to generate agent cards (service returned null).');
|
|
1287
|
-
this.toastrService.error({ subtitle: 'Could not prepare the AI chat session.', title: 'Error' });
|
|
1288
|
-
}
|
|
1414
|
+
const conversationSettings = await this.lessonConversationService.startAI(currentLesson, this.settings());
|
|
1415
|
+
if (conversationSettings) {
|
|
1416
|
+
this.uiStateService.chatDrawerVisible.set(true);
|
|
1289
1417
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
this.toastrService.error({ subtitle: 'An error occurred while preparing the AI chat.', title: 'Error' });
|
|
1418
|
+
else {
|
|
1419
|
+
this.toastrService.error({ subtitle: 'Could not prepare the AI chat session.', title: 'Error' });
|
|
1293
1420
|
}
|
|
1294
1421
|
}
|
|
1295
1422
|
onVisibleChange(isVisible) {
|
|
1296
1423
|
if (isVisible === false) {
|
|
1297
|
-
this.
|
|
1424
|
+
this.uiStateService.chatDrawerVisible.set(false);
|
|
1298
1425
|
}
|
|
1299
1426
|
}
|
|
1300
|
-
handleGoalCompleted(event) {
|
|
1427
|
+
async handleGoalCompleted(event = {}) {
|
|
1301
1428
|
console.log('Goal completed:', event);
|
|
1302
1429
|
const lesson = this.lesson();
|
|
1303
|
-
const
|
|
1304
|
-
this.
|
|
1430
|
+
const takenLesson = { id: lesson.id, goalCompleted: true, score: 100, status: 'finished', lastAccess: new Date() };
|
|
1431
|
+
const res = await this.lessonsService.saveTakenLesson(takenLesson);
|
|
1432
|
+
if (res) {
|
|
1433
|
+
// this.lessonsMemStateService.updateUserState(res);
|
|
1434
|
+
}
|
|
1305
1435
|
this.toastrService.success({ subtitle: '¡Has completado la lección! , pero puedes seguir conversando', title: '¡Muy bien, guardaremos tu progreso!' });
|
|
1306
1436
|
}
|
|
1307
1437
|
onChatMessage(event) {
|
|
@@ -1318,29 +1448,25 @@ class DCLessonRendererComponent {
|
|
|
1318
1448
|
console.log('Unhandled chat event type:', event.type);
|
|
1319
1449
|
}
|
|
1320
1450
|
}
|
|
1321
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1322
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
1451
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1452
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wordClicked: "wordClicked" }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (lesson()?.format === 'markdown') {\n<h3>Mostrando markdown</h3>\n\n<markdown>\n {{ lesson()?.markdown }}\n</markdown>\n} @else {\n<h5>Lesson not available</h5>\n<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n}\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"lessonConversationService.conversationFlow()\"\n [conversationSettings]=\"lessonConversationService.conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "conversationFlow", "agentCard", "parseDict"], outputs: ["chatEvent", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$2.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1323
1453
|
}
|
|
1324
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1454
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
|
|
1325
1455
|
type: Component,
|
|
1326
|
-
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(
|
|
1456
|
+
args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule, MarkdownComponent], template: "@if (lesson()?.format === 'markdown') {\n<h3>Mostrando markdown</h3>\n\n<markdown>\n {{ lesson()?.markdown }}\n</markdown>\n} @else {\n<h5>Lesson not available</h5>\n<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n}\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(uiStateService.chatDrawerVisible()) {\n<p-drawer\n header=\"Conversation\"\n [visible]=\"uiStateService.chatDrawerVisible()\"\n (visibleChange)=\"onVisibleChange($event)\"\n position=\"bottom\"\n styleClass=\"app-bottom-overlay\">\n <dc-chat\n [conversationFlow]=\"lessonConversationService.conversationFlow()\"\n [conversationSettings]=\"lessonConversationService.conversationSettings()\"\n (goalCompleted)=\"handleGoalCompleted($event)\"\n (chatEvent)=\"onChatMessage($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: ["::ng-deep .targetclass *:not(h1,h2,h3,h4,h5,h6){font-family:\"math\",sans-serif,system-ui,monospace!important}\n"] }]
|
|
1327
1457
|
}], ctorParameters: () => [], propDecorators: { wordClicked: [{
|
|
1328
1458
|
type: Output
|
|
1329
|
-
}], dynamicLesson: [{
|
|
1330
|
-
type: ViewChild,
|
|
1331
|
-
args: ['dynamicLesson', { static: true }]
|
|
1332
1459
|
}] } });
|
|
1333
1460
|
|
|
1334
1461
|
class LessonNotionService {
|
|
1335
1462
|
constructor() {
|
|
1336
1463
|
this.#notionService = inject(NOTION_SERVICE_TOKEN);
|
|
1337
|
-
this
|
|
1464
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
1338
1465
|
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1339
1466
|
// Keep track of loading state specific to Notion operations
|
|
1340
|
-
this.isLoading = signal(false);
|
|
1467
|
+
this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
1341
1468
|
}
|
|
1342
1469
|
#notionService;
|
|
1343
|
-
#lessonService;
|
|
1344
1470
|
#toastService;
|
|
1345
1471
|
/**
|
|
1346
1472
|
* Extracts the Notion Page ID from a URL.
|
|
@@ -1374,14 +1500,17 @@ class LessonNotionService {
|
|
|
1374
1500
|
}
|
|
1375
1501
|
const updatedLesson = {
|
|
1376
1502
|
...lesson,
|
|
1377
|
-
|
|
1378
|
-
...(lesson.
|
|
1379
|
-
|
|
1503
|
+
extensions: {
|
|
1504
|
+
...(lesson.extensions || {}),
|
|
1505
|
+
extras: {
|
|
1506
|
+
...(lesson.extensions?.['extras'] || {}),
|
|
1507
|
+
notionPageId: notionPageId,
|
|
1508
|
+
},
|
|
1380
1509
|
},
|
|
1381
1510
|
};
|
|
1382
1511
|
this.isLoading.set(true);
|
|
1383
1512
|
try {
|
|
1384
|
-
const savedLesson = await this
|
|
1513
|
+
const savedLesson = await this.lessonsService.postLesson(updatedLesson);
|
|
1385
1514
|
this.#toastService.success({ title: 'Listo', subtitle: 'Se enlazó la lección con Notion.' });
|
|
1386
1515
|
return savedLesson;
|
|
1387
1516
|
}
|
|
@@ -1407,10 +1536,10 @@ class LessonNotionService {
|
|
|
1407
1536
|
if (!currentLesson)
|
|
1408
1537
|
return null;
|
|
1409
1538
|
let notionPageId = null;
|
|
1410
|
-
if (currentLesson.extras?.notionPageId) {
|
|
1411
|
-
const useExisting = confirm(`Ya tenemos el id ${currentLesson.extras
|
|
1539
|
+
if (currentLesson.extensions?.['extras']?.['notionPageId']) {
|
|
1540
|
+
const useExisting = confirm(`Ya tenemos el id ${currentLesson.extensions?.['extras']?.['notionPageId']} ¿Quieres usar este id para importar?`);
|
|
1412
1541
|
if (useExisting) {
|
|
1413
|
-
notionPageId = currentLesson.extras
|
|
1542
|
+
notionPageId = currentLesson.extensions?.['extras']?.['notionPageId'];
|
|
1414
1543
|
}
|
|
1415
1544
|
else {
|
|
1416
1545
|
const inputUrl = prompt('Ingresa la NUEVA URL de Notion para importar (este ID NO se guardará automáticamente si la lección ya existe)');
|
|
@@ -1465,7 +1594,7 @@ class LessonNotionService {
|
|
|
1465
1594
|
async improveLessonWithNotionAI(lesson) {
|
|
1466
1595
|
if (!lesson)
|
|
1467
1596
|
return;
|
|
1468
|
-
const notionId = lesson.extras?.notionPageId;
|
|
1597
|
+
const notionId = lesson.extensions?.['extras']?.['notionPageId'];
|
|
1469
1598
|
if (!notionId) {
|
|
1470
1599
|
this.#toastService.warn({ title: 'Sin ID de Notion', subtitle: 'Enlaza la lección con Notion primero.' });
|
|
1471
1600
|
return;
|
|
@@ -1489,22 +1618,21 @@ class LessonNotionService {
|
|
|
1489
1618
|
this.isLoading.set(false);
|
|
1490
1619
|
}
|
|
1491
1620
|
}
|
|
1492
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1493
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1621
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonNotionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1622
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonNotionService }); }
|
|
1494
1623
|
}
|
|
1495
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1624
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonNotionService, decorators: [{
|
|
1496
1625
|
type: Injectable
|
|
1497
1626
|
}] });
|
|
1498
1627
|
|
|
1499
1628
|
// import { UserDataExchangeService } from '@dataclouder/ngx-agent-cards';
|
|
1500
1629
|
class LessonUtilsService {
|
|
1501
1630
|
constructor() {
|
|
1502
|
-
this
|
|
1631
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
1503
1632
|
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1504
1633
|
this.#agentService = inject(CONVERSATION_AI_TOKEN);
|
|
1505
1634
|
this.loadingBarService = inject(LoadingBarService);
|
|
1506
1635
|
}
|
|
1507
|
-
#lessonService;
|
|
1508
1636
|
#toastService;
|
|
1509
1637
|
#agentService;
|
|
1510
1638
|
/**
|
|
@@ -1529,10 +1657,11 @@ class LessonUtilsService {
|
|
|
1529
1657
|
lessonSignal.update((currentLesson) => {
|
|
1530
1658
|
if (!currentLesson)
|
|
1531
1659
|
return undefined;
|
|
1532
|
-
const
|
|
1660
|
+
const assets = { ...(currentLesson.assets ?? {}) };
|
|
1661
|
+
assets.banner = imageUploaded;
|
|
1533
1662
|
return {
|
|
1534
1663
|
...currentLesson,
|
|
1535
|
-
|
|
1664
|
+
assets,
|
|
1536
1665
|
};
|
|
1537
1666
|
});
|
|
1538
1667
|
}
|
|
@@ -1550,9 +1679,9 @@ class LessonUtilsService {
|
|
|
1550
1679
|
}
|
|
1551
1680
|
// No need to save here, component should ensure it's saved before calling.
|
|
1552
1681
|
try {
|
|
1553
|
-
await this
|
|
1682
|
+
await this.lessonsService.postGenerateByAI(lessonId);
|
|
1554
1683
|
// Re-fetch the lesson data to get AI updates
|
|
1555
|
-
const updatedLesson = await this
|
|
1684
|
+
const updatedLesson = await this.lessonsService.getLesson(lessonId);
|
|
1556
1685
|
if (updatedLesson) {
|
|
1557
1686
|
this.#toastService.success({ title: 'IA completada', subtitle: 'Lección actualizada con IA.' });
|
|
1558
1687
|
return updatedLesson;
|
|
@@ -1583,7 +1712,7 @@ class LessonUtilsService {
|
|
|
1583
1712
|
}
|
|
1584
1713
|
try {
|
|
1585
1714
|
this.loadingBarService.showIndeterminate();
|
|
1586
|
-
const textPrompt = this
|
|
1715
|
+
const textPrompt = this.lessonsService.getPrompts().content(lesson);
|
|
1587
1716
|
const messages = [{ content: textPrompt, role: ChatRole.User }];
|
|
1588
1717
|
const response = await this.#agentService.callChatCompletion({ messages, model: { provider: 'google' } });
|
|
1589
1718
|
let improvedMarkdown = response.content?.trim() ?? null;
|
|
@@ -1635,7 +1764,7 @@ class LessonUtilsService {
|
|
|
1635
1764
|
this.#toastService.warn({ title: 'Texto Vacío', subtitle: 'No se pudo extraer texto útil del contenido de la lección.' });
|
|
1636
1765
|
return null;
|
|
1637
1766
|
}
|
|
1638
|
-
const descriptionPrompt = this
|
|
1767
|
+
const descriptionPrompt = this.lessonsService.getPrompts().description(lesson);
|
|
1639
1768
|
const messages = [{ content: descriptionPrompt, role: ChatRole.User }];
|
|
1640
1769
|
const response = await this.#agentService.callChatCompletion({ messages, model: { provider: 'google' } });
|
|
1641
1770
|
let generatedDescription = response.content?.trim() ?? null;
|
|
@@ -1742,10 +1871,10 @@ class LessonUtilsService {
|
|
|
1742
1871
|
dynamicComponents: cleanedDynamicComponents,
|
|
1743
1872
|
};
|
|
1744
1873
|
}
|
|
1745
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1746
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1874
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonUtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1875
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonUtilsService, providedIn: 'root' }); }
|
|
1747
1876
|
}
|
|
1748
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1877
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonUtilsService, decorators: [{
|
|
1749
1878
|
type: Injectable,
|
|
1750
1879
|
args: [{
|
|
1751
1880
|
providedIn: 'root', // Provide globally or in a specific module if preferred
|
|
@@ -1775,10 +1904,10 @@ class DynamicComponentsBuilderService {
|
|
|
1775
1904
|
});
|
|
1776
1905
|
return dialogRef;
|
|
1777
1906
|
}
|
|
1778
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1779
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1907
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1908
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsBuilderService, providedIn: 'root' }); }
|
|
1780
1909
|
}
|
|
1781
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1910
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DynamicComponentsBuilderService, decorators: [{
|
|
1782
1911
|
type: Injectable,
|
|
1783
1912
|
args: [{
|
|
1784
1913
|
providedIn: 'root',
|
|
@@ -1812,10 +1941,10 @@ class DCLessonComponentAdderComponent {
|
|
|
1812
1941
|
console.warn(`Dialog could not be opened for type via component: ${type}`);
|
|
1813
1942
|
}
|
|
1814
1943
|
}
|
|
1815
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1816
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
1944
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonComponentAdderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1945
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: DCLessonComponentAdderComponent, isStandalone: true, selector: "dc-lesson-component-adder", outputs: { componentAdded: "componentAdded" }, providers: [DialogService], ngImport: i0, template: "<span>Componentes: </span>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap\">\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Selector)\"\n pTooltip=\"Agrega un selector con multiples opciones\"\n tooltipPosition=\"bottom\">\n Selector\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.Speaker)\"\n pTooltip=\"Para que una palabra o frase sea reproducible\"\n tooltipPosition=\"bottom\">\n Speaker\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TextWriter)\"\n pTooltip=\"Escribe una respuesta en un cuadro de texto\"\n tooltipPosition=\"bottom\">\n Text\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.VerbSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de un verbo\"\n tooltipPosition=\"bottom\">\n Verb\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.WordSummary)\"\n pTooltip=\"Muestra la informaci\u00F3n de una palabra\"\n tooltipPosition=\"bottom\">\n Palabra\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.TranslationSwitcher)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Traducci\u00F3n\n </p-button>\n <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.PlayWord)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Play Word\n </p-button>\n <!-- Add other buttons here if needed, following the same pattern -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] }); }
|
|
1817
1946
|
}
|
|
1818
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1947
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonComponentAdderComponent, decorators: [{
|
|
1819
1948
|
type: Component,
|
|
1820
1949
|
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 <p-button\n severity=\"info\"\n (click)=\"openComponentBuilder(lessonComponentEnum.PlayWord)\"\n pTooltip=\"Muestra el texto pero al pica cambia de idioma\"\n tooltipPosition=\"bottom\">\n Play Word\n </p-button>\n <!-- Add other buttons here if needed, following the same pattern -->\n</div>\n" }]
|
|
1821
1950
|
}], propDecorators: { componentAdded: [{
|
|
@@ -1824,111 +1953,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1824
1953
|
|
|
1825
1954
|
class DCLessonMetadataEditorComponent {
|
|
1826
1955
|
constructor() {
|
|
1827
|
-
// Use signal for input lesson data
|
|
1828
|
-
this.lesson = signal(undefined); // The lesson data itself
|
|
1829
|
-
this.isLoadingLesson = signal(false); // Shared loading state
|
|
1830
1956
|
// Outputs for actions
|
|
1831
1957
|
this.saveRequest = new EventEmitter();
|
|
1832
1958
|
this.importNotionRequest = new EventEmitter();
|
|
1833
1959
|
this.improveNotionRequest = new EventEmitter();
|
|
1834
|
-
// Removed generateAIRequest Output as it's handled internally now
|
|
1835
|
-
// Output removed as the component now updates the input signal directly.
|
|
1836
|
-
// @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
|
|
1837
1960
|
// Injected Services
|
|
1838
1961
|
this.#lessonUtilsService = inject(LessonUtilsService);
|
|
1839
1962
|
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1840
|
-
this
|
|
1841
|
-
this
|
|
1963
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
1964
|
+
this.turndownService = new TurndownService(); // Instantiate TurndownService
|
|
1842
1965
|
}
|
|
1843
|
-
// Removed generateAIRequest Output as it's handled internally now
|
|
1844
|
-
// Output removed as the component now updates the input signal directly.
|
|
1845
|
-
// @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
|
|
1846
1966
|
// Injected Services
|
|
1847
1967
|
#lessonUtilsService;
|
|
1848
1968
|
#toastService;
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
this.lesson.update((current) => {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
});
|
|
1863
|
-
//
|
|
1864
|
-
//
|
|
1865
|
-
}
|
|
1866
|
-
//
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1875
|
-
// Method to handle property changes for APP EXTENSION properties
|
|
1876
|
-
onAppExtensionPropChange(property, value) {
|
|
1877
|
-
this.lesson.update((current) => {
|
|
1878
|
-
if (!current)
|
|
1879
|
-
return undefined;
|
|
1880
|
-
// Ensure appExtension exists, initialize if not
|
|
1881
|
-
// Convert value to number specifically for 'level'
|
|
1882
|
-
const finalValue = property === 'level' ? Number(value) : value;
|
|
1883
|
-
const updatedAppExtension = { ...(current.appExtension ?? {}), [property]: finalValue };
|
|
1884
|
-
return { ...current, appExtension: updatedAppExtension };
|
|
1885
|
-
});
|
|
1886
|
-
}
|
|
1887
|
-
// Methods to emit action requests
|
|
1888
|
-
emitSaveRequest() {
|
|
1889
|
-
this.saveRequest.emit();
|
|
1890
|
-
}
|
|
1891
|
-
emitImportNotionRequest() {
|
|
1892
|
-
this.importNotionRequest.emit();
|
|
1893
|
-
}
|
|
1894
|
-
emitImproveNotionRequest() {
|
|
1895
|
-
this.improveNotionRequest.emit();
|
|
1969
|
+
ngOnInit() {
|
|
1970
|
+
// console.log(this.lesson(), this.form);
|
|
1971
|
+
}
|
|
1972
|
+
onManageablePropertyChange(property, value) {
|
|
1973
|
+
// this.lesson.update((current) => {
|
|
1974
|
+
// if (!current) return undefined;
|
|
1975
|
+
// const updatedManageable = { ...(current.manageable ?? {}), [property]: value };
|
|
1976
|
+
// return { ...current, manageable: updatedManageable as IManageable };
|
|
1977
|
+
// });
|
|
1978
|
+
}
|
|
1979
|
+
onAuditablePropertyChange(property, value) {
|
|
1980
|
+
// this.lesson.update((current) => {
|
|
1981
|
+
// if (!current) return undefined;
|
|
1982
|
+
// const updatedAuditable = { ...(current.auditable ?? {}), [property]: value };
|
|
1983
|
+
// return { ...current, auditable: updatedAuditable as IAuditable };
|
|
1984
|
+
// });
|
|
1985
|
+
}
|
|
1986
|
+
// New methods to handle events with proper casting
|
|
1987
|
+
handlePromptInputChange(event) {
|
|
1988
|
+
const target = event.target;
|
|
1989
|
+
this.onAuditablePropertyChange('prompt', target.value);
|
|
1990
|
+
}
|
|
1991
|
+
handleStatusChange(event) {
|
|
1992
|
+
const target = event.target;
|
|
1993
|
+
this.onManageablePropertyChange('status', target.checked ? 'published' : 'draft');
|
|
1896
1994
|
}
|
|
1897
1995
|
/**
|
|
1898
1996
|
* Generates lesson content using AI, saving the current state first.
|
|
1899
1997
|
* Moved from DCLessonEditorComponent.
|
|
1900
1998
|
*/
|
|
1901
1999
|
async generateByAI() {
|
|
1902
|
-
const currentLesson = this.lesson
|
|
2000
|
+
const currentLesson = this.lesson; // Get current value
|
|
1903
2001
|
if (!currentLesson?.id) {
|
|
1904
2002
|
this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
|
|
1905
2003
|
return;
|
|
1906
2004
|
}
|
|
1907
|
-
this.isLoadingLesson
|
|
2005
|
+
this.isLoadingLesson = true;
|
|
1908
2006
|
try {
|
|
1909
2007
|
const rawHtmlContent = currentLesson.textCoded || '';
|
|
1910
2008
|
if (!rawHtmlContent) {
|
|
1911
2009
|
console.warn('No HTML content found in lesson to process. taking just description');
|
|
1912
2010
|
this.#toastService.info({ title: 'Contenido lección desde 0', subtitle: 'Solo se usará el prompt' });
|
|
1913
|
-
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson
|
|
2011
|
+
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson, 'Create content from description');
|
|
1914
2012
|
// Convert and save the generated content
|
|
1915
2013
|
await this._convertMarkdownToHtmlAndSave(improvedMarkdown); // Use extracted method
|
|
1916
2014
|
}
|
|
1917
2015
|
else {
|
|
1918
2016
|
// Clean orphaned and Save before Improve
|
|
1919
2017
|
const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLesson);
|
|
1920
|
-
const savedLesson = await this
|
|
2018
|
+
const savedLesson = await this.lessonsService.postLesson(lessonToSave);
|
|
1921
2019
|
if (!savedLesson) {
|
|
1922
2020
|
this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudo guardar antes de generar con IA.' });
|
|
1923
2021
|
throw new Error('Failed to save before AI generation');
|
|
1924
2022
|
}
|
|
1925
|
-
this.lesson
|
|
2023
|
+
this.lesson = savedLesson;
|
|
1926
2024
|
// Replace encoded JSON with actual text before Markdown conversion
|
|
1927
2025
|
const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
|
|
1928
2026
|
// Convert the processed HTML (with text instead of JSON) to Markdown
|
|
1929
|
-
const markdownText = this
|
|
2027
|
+
const markdownText = this.turndownService.turndown(processedHtmlContent);
|
|
1930
2028
|
// Use the updated lesson signal value for AI improvement
|
|
1931
|
-
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson
|
|
2029
|
+
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson, markdownText);
|
|
1932
2030
|
// Convert and save the improved content
|
|
1933
2031
|
await this._convertMarkdownToHtmlAndSave(improvedMarkdown); // Use extracted method
|
|
1934
2032
|
}
|
|
@@ -1939,7 +2037,7 @@ class DCLessonMetadataEditorComponent {
|
|
|
1939
2037
|
// this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
|
|
1940
2038
|
}
|
|
1941
2039
|
finally {
|
|
1942
|
-
this.isLoadingLesson
|
|
2040
|
+
this.isLoadingLesson = false; // Stop loading
|
|
1943
2041
|
}
|
|
1944
2042
|
}
|
|
1945
2043
|
/**
|
|
@@ -1975,12 +2073,12 @@ class DCLessonMetadataEditorComponent {
|
|
|
1975
2073
|
// Convert the improved Markdown back to HTML before setting it
|
|
1976
2074
|
const improvedHtml = marked(improvedMarkdown);
|
|
1977
2075
|
// Update the signal directly
|
|
1978
|
-
this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
|
|
2076
|
+
// this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
|
|
1979
2077
|
// Save the AI-generated content
|
|
1980
2078
|
// Ensure lesson() is not undefined before saving
|
|
1981
|
-
const lessonToSave = this.lesson
|
|
2079
|
+
const lessonToSave = this.lesson;
|
|
1982
2080
|
if (lessonToSave) {
|
|
1983
|
-
await this
|
|
2081
|
+
await this.lessonsService.postLesson(lessonToSave);
|
|
1984
2082
|
this.#toastService.success({ title: 'Contenido generado', subtitle: 'Se generó y guardó el contenido con IA.' });
|
|
1985
2083
|
}
|
|
1986
2084
|
else {
|
|
@@ -2003,37 +2101,40 @@ class DCLessonMetadataEditorComponent {
|
|
|
2003
2101
|
* and updates the lesson signal if successful.
|
|
2004
2102
|
*/
|
|
2005
2103
|
async triggerGenerateDescriptionAI() {
|
|
2006
|
-
const currentLesson = this.lesson
|
|
2104
|
+
const currentLesson = this.lesson;
|
|
2007
2105
|
if (!currentLesson) {
|
|
2008
2106
|
this.#toastService.warn({ title: 'Lección no cargada', subtitle: 'Espera a que la lección se cargue.' });
|
|
2009
2107
|
return;
|
|
2010
2108
|
}
|
|
2011
2109
|
const generatedDescription = await this.#lessonUtilsService.generateDescriptionWithAI(currentLesson);
|
|
2012
2110
|
if (generatedDescription) {
|
|
2013
|
-
//
|
|
2014
|
-
this.onMetadataPropertyChange('description', generatedDescription);
|
|
2111
|
+
// this.form.controls['description'].setValue(generatedDescription);
|
|
2015
2112
|
}
|
|
2016
2113
|
}
|
|
2017
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2018
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
2114
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2115
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { form: "form", lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest" }, ngImport: i0, template: "<div>\n <!-- <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div> -->\n\n <div>\n <div>\n <span>Nombre de La lecci\u00F3n</span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['name']\" type=\"text\" placeholder=\"Agrega un nombre\" />\n </div>\n <div>\n <span>T\u00EDtulo </span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['title']\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n\n <div style=\"margin-top: 4px\">\n <span>Descripci\u00F3n </span>\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['description']\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [value]=\"lesson?.auditable?.prompt || ''\"\n (input)=\"handlePromptInputChange($event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\" />\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input pInputText [value]=\"lesson?.extensions?.['level'] || ''\" type=\"number\" placeholder=\"Nivel\" style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (lesson?.extensions) {\n <div>\n {{ lesson?.extensions?.['baseLang'] | flagEmoji }} -> {{ lesson?.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ lesson?.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ lesson?.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "ngmodule", type: // Added Pipe
|
|
2116
|
+
InputGroupModule }, { kind: "component", type: i5.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i6.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "pipe", type: // Added TooltipModule
|
|
2019
2117
|
FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
|
|
2020
|
-
|
|
2021
|
-
InputGroupModule }, { kind: "component", type: i5.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["style", "styleClass"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i6$1.Divider, selector: "p-divider", inputs: ["style", "styleClass", "layout", "type", "align"] }] }); }
|
|
2118
|
+
LangDescTranslation, name: "langDesc" }] }); }
|
|
2022
2119
|
}
|
|
2023
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2120
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonMetadataEditorComponent, decorators: [{
|
|
2024
2121
|
type: Component,
|
|
2025
2122
|
args: [{ selector: 'dc-lesson-metadata-editor', standalone: true, imports: [
|
|
2026
2123
|
CommonModule,
|
|
2027
2124
|
FormsModule,
|
|
2028
2125
|
ButtonModule,
|
|
2029
2126
|
InputTextModule,
|
|
2127
|
+
ReactiveFormsModule,
|
|
2030
2128
|
TooltipModule, // Added TooltipModule
|
|
2031
2129
|
FlagLanguagePipe, // Added Pipe
|
|
2032
|
-
|
|
2130
|
+
LangDescTranslation, // Added Pipe
|
|
2033
2131
|
InputGroupModule,
|
|
2034
2132
|
DividerModule,
|
|
2035
|
-
], template: "
|
|
2036
|
-
}], propDecorators: {
|
|
2133
|
+
], template: "<div>\n <!-- <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div> -->\n\n <div>\n <div>\n <span>Nombre de La lecci\u00F3n</span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['name']\" type=\"text\" placeholder=\"Agrega un nombre\" />\n </div>\n <div>\n <span>T\u00EDtulo </span>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['title']\" type=\"text\" placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n\n <div style=\"margin-top: 4px\">\n <span>Descripci\u00F3n </span>\n <p-inputgroup>\n <input pInputText style=\"width: 100%\" [formControl]=\"form.controls['description']\" type=\"text\" placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [value]=\"lesson?.auditable?.prompt || ''\"\n (input)=\"handlePromptInputChange($event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson\" (click)=\"generateByAI()\" />\n </div>\n\n <p-divider />\n\n <div style=\"display: flex; align-items: center; margin-top: 10px; gap: 10px\">\n <input pInputText [value]=\"lesson?.extensions?.['level'] || ''\" type=\"number\" placeholder=\"Nivel\" style=\"width: 80px\" />\n\n <!-- Access signal values -->\n @if (lesson?.extensions) {\n <div>\n {{ lesson?.extensions?.['baseLang'] | flagEmoji }} -> {{ lesson?.extensions?.['targetLang'] | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ lesson?.extensions?.['baseLang'] | langDesc : 'es' }} que aprenden\n {{ lesson?.extensions?.['targetLang'] | langDesc : 'es' }}\n </div>\n }\n </div>\n</div>\n" }]
|
|
2134
|
+
}], propDecorators: { form: [{
|
|
2135
|
+
type: Input,
|
|
2136
|
+
args: [{ required: true }]
|
|
2137
|
+
}], lesson: [{
|
|
2037
2138
|
type: Input,
|
|
2038
2139
|
args: [{ required: true }]
|
|
2039
2140
|
}], isLoadingLesson: [{
|
|
@@ -2047,205 +2148,91 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2047
2148
|
type: Output
|
|
2048
2149
|
}] } });
|
|
2049
2150
|
|
|
2050
|
-
|
|
2051
|
-
const LESSONS_BASE_PATH = 'api/lesson'; // Example base path
|
|
2052
|
-
class DefaultLessonsService {
|
|
2151
|
+
class LessonFormEditorService {
|
|
2053
2152
|
constructor() {
|
|
2054
|
-
this.
|
|
2055
|
-
|
|
2056
|
-
this.
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
updateLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming PUT to /lessons/:id
|
|
2061
|
-
deleteLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming DELETE to /lessons/:id
|
|
2062
|
-
generateLesson: `${LESSONS_BASE_PATH}/generate`, // Placeholder
|
|
2063
|
-
generateByAI: `${LESSONS_BASE_PATH}/generate-ai`, // Placeholder
|
|
2064
|
-
improveMDWithAI: `${LESSONS_BASE_PATH}/improve-markdown-ai`, // Placeholder
|
|
2065
|
-
QueryLessons: 'api/lesson/query',
|
|
2066
|
-
Lesson: 'api/lesson',
|
|
2067
|
-
SaveLesson: 'api/lesson-polilan',
|
|
2068
|
-
GetPublicLessons: 'api/lesson/publicLessons',
|
|
2069
|
-
GetUnpublishedLessons: 'api/lesson/unpublished',
|
|
2070
|
-
TakenLesson: 'api/lesson/taken',
|
|
2071
|
-
DeleteLesson: 'api/lesson',
|
|
2072
|
-
Base: 'api/lesson',
|
|
2073
|
-
GenerateBanner: 'api/lesson/generate-banner',
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
saveTakenLesson(lesson) {
|
|
2077
|
-
// Not sure how to implement this yet.
|
|
2078
|
-
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2079
|
-
}
|
|
2080
|
-
// --- Method Implementations ---
|
|
2081
|
-
async getLessons(paginator) {
|
|
2082
|
-
// Assuming paginator is the body for a POST request based on the example
|
|
2083
|
-
return this.httpCoreService.post(this.endpoints.queryLessons, paginator || {});
|
|
2084
|
-
}
|
|
2085
|
-
async getLesson(id) {
|
|
2086
|
-
return this.httpCoreService.get(this.endpoints.getLesson(id));
|
|
2087
|
-
}
|
|
2088
|
-
async postLesson(lesson) {
|
|
2089
|
-
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2090
|
-
}
|
|
2091
|
-
async updateLesson(lesson) {
|
|
2092
|
-
if (!lesson._id) {
|
|
2093
|
-
throw new Error('Lesson ID is required for update.');
|
|
2094
|
-
}
|
|
2095
|
-
// Assuming _id is the identifier
|
|
2096
|
-
return this.httpCoreService.put(this.endpoints.updateLesson(lesson._id), lesson);
|
|
2097
|
-
}
|
|
2098
|
-
async deleteLesson(id) {
|
|
2099
|
-
return this.httpCoreService.delete(this.endpoints.deleteLesson(id));
|
|
2100
|
-
}
|
|
2101
|
-
async generateLesson(lesson) {
|
|
2102
|
-
// This endpoint might need specific data or structure
|
|
2103
|
-
return this.httpCoreService.post(this.endpoints.generateLesson, lesson);
|
|
2104
|
-
}
|
|
2105
|
-
async postGenerateByAI(id) {
|
|
2106
|
-
return this.httpCoreService.post(this.endpoints.generateByAI, { id });
|
|
2107
|
-
}
|
|
2108
|
-
async postImproveMDWithAI(lessonId, markdownText) {
|
|
2109
|
-
return this.httpCoreService.post(this.endpoints.improveMDWithAI, { id: lessonId, markdown: markdownText });
|
|
2153
|
+
this.fb = inject(FormBuilder);
|
|
2154
|
+
this.formUtils = inject(FormUtilsService);
|
|
2155
|
+
this.formatOptions = [
|
|
2156
|
+
{ label: 'HTML', value: 'html' },
|
|
2157
|
+
{ label: 'Markdown', value: 'markdown' },
|
|
2158
|
+
];
|
|
2110
2159
|
}
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2160
|
+
createLessonForm() {
|
|
2161
|
+
return this.fb.group({
|
|
2162
|
+
version: ['1.0'],
|
|
2163
|
+
id: [''],
|
|
2164
|
+
name: [''],
|
|
2165
|
+
title: [''],
|
|
2166
|
+
description: [''],
|
|
2167
|
+
format: ['html'],
|
|
2168
|
+
lang: [''],
|
|
2169
|
+
characterCard: [],
|
|
2170
|
+
conversationSettings: [],
|
|
2171
|
+
metaApp: [],
|
|
2172
|
+
conversationFlow: [],
|
|
2173
|
+
textCoded: [''],
|
|
2174
|
+
manageable: this.formUtils.createManageableFormGroup(),
|
|
2175
|
+
learnable: this.formUtils.createLearnableFormGroup(),
|
|
2123
2176
|
});
|
|
2124
|
-
// Remove HTML tags
|
|
2125
|
-
let text = lessonHtml.replace(/<[^>]*>/g, ' ');
|
|
2126
|
-
// Remove style and script content
|
|
2127
|
-
text = text.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, ' ');
|
|
2128
|
-
text = text.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, ' ');
|
|
2129
|
-
// Decode HTML entities
|
|
2130
|
-
text = text.replace(/ /g, ' ');
|
|
2131
|
-
text = text.replace(/&/g, '&');
|
|
2132
|
-
text = text.replace(/</g, '<');
|
|
2133
|
-
text = text.replace(/>/g, '>');
|
|
2134
|
-
// Remove extra whitespace
|
|
2135
|
-
text = text.replace(/\s+/g, ' ').trim();
|
|
2136
|
-
return text;
|
|
2137
|
-
}
|
|
2138
|
-
generateBanner(prompt, lessonId) {
|
|
2139
|
-
return this.httpCoreService.post(this.endpoints.GenerateBanner, { prompt, lessonId });
|
|
2140
|
-
}
|
|
2141
|
-
getPrompts() {
|
|
2142
|
-
return null;
|
|
2143
2177
|
}
|
|
2144
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2145
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2178
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2179
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, providedIn: 'root' }); }
|
|
2146
2180
|
}
|
|
2147
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2181
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonFormEditorService, decorators: [{
|
|
2148
2182
|
type: Injectable,
|
|
2149
2183
|
args: [{
|
|
2150
2184
|
providedIn: 'root',
|
|
2151
2185
|
}]
|
|
2152
2186
|
}] });
|
|
2153
2187
|
|
|
2154
|
-
class DCLessonEditorComponent {
|
|
2155
|
-
// Services
|
|
2156
|
-
#activatedRoute; // Re-inject as it's needed for navigation
|
|
2157
|
-
#lessonNotionService;
|
|
2158
|
-
#lessonUtilsService;
|
|
2159
|
-
#router;
|
|
2160
|
-
#lessonService;
|
|
2161
|
-
#toastService;
|
|
2162
|
-
#loadingBarService;
|
|
2188
|
+
class DCLessonEditorComponent extends EntityBaseFormComponent {
|
|
2163
2189
|
constructor() {
|
|
2190
|
+
super(...arguments);
|
|
2191
|
+
this.lessonFormEditorService = inject(LessonFormEditorService);
|
|
2192
|
+
this.markdownService = inject(MarkdownService);
|
|
2193
|
+
this.form = this.lessonFormEditorService.createLessonForm();
|
|
2194
|
+
this.formatOptions = this.lessonFormEditorService.formatOptions;
|
|
2195
|
+
this.entityCommunicationService = inject(LESSONS_TOKEN);
|
|
2196
|
+
this.htmlTemporal = '';
|
|
2164
2197
|
// Services
|
|
2165
|
-
this
|
|
2166
|
-
this
|
|
2167
|
-
this
|
|
2168
|
-
this
|
|
2169
|
-
this
|
|
2170
|
-
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
2171
|
-
this.#loadingBarService = inject(LoadingBarService);
|
|
2198
|
+
this.activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
|
|
2199
|
+
this.lessonNotionService = inject(LessonNotionService);
|
|
2200
|
+
this.lessonUtilsService = inject(LessonUtilsService);
|
|
2201
|
+
this.lessonsService = inject(LESSONS_TOKEN, { optional: true }) ?? inject(DefaultLessonsService);
|
|
2202
|
+
this.loadingBarService = inject(LoadingBarService);
|
|
2172
2203
|
this.defaultLessonsService = inject(DefaultLessonsService);
|
|
2173
2204
|
this.promptService = inject(PromptService);
|
|
2174
2205
|
this.ngxVertexService = inject(NgxVertexService);
|
|
2175
|
-
this.cdr = inject(ChangeDetectorRef);
|
|
2176
2206
|
this.dynamicComponentsBuilderService = inject(DynamicComponentsBuilderService);
|
|
2177
|
-
|
|
2178
|
-
this.lessonId = toSignal(inject(ActivatedRoute).paramMap.pipe(map((params) => params.get('id'))));
|
|
2179
|
-
this.lesson = signal(undefined); // Initialize as undefined
|
|
2180
|
-
this.isLoadingLesson = signal(false);
|
|
2181
|
-
// Computed Signals
|
|
2182
|
-
this.coverImageUrl = computed(() => {
|
|
2183
|
-
// Priority Order 1 Metadata Banner, 2 Banner, 3 Media First Images, 4 Default Banner TODO: reveme banner after migration to Content
|
|
2184
|
-
const currentLesson = this.lesson();
|
|
2185
|
-
if (currentLesson?.metadata?.banner?.url) {
|
|
2186
|
-
return currentLesson.metadata.banner.url;
|
|
2187
|
-
}
|
|
2188
|
-
else if (currentLesson?.banner?.url) {
|
|
2189
|
-
return currentLesson.banner.url;
|
|
2190
|
-
}
|
|
2191
|
-
else if (currentLesson?.media?.images?.find((img) => img.type === 'cover')) {
|
|
2192
|
-
// 3 Media First Images
|
|
2193
|
-
return currentLesson.media.images.find((img) => img.type === 'cover')?.url;
|
|
2194
|
-
}
|
|
2195
|
-
return '/assets/images/default_banner.webp';
|
|
2196
|
-
});
|
|
2207
|
+
this.isLoadingLesson = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingLesson" }] : []));
|
|
2197
2208
|
// Computed signal to get dynamic components as an array for easier iteration in the template
|
|
2198
2209
|
this.dynamicComponentsArray = computed(() => {
|
|
2199
|
-
const currentLesson = this.lesson();
|
|
2200
|
-
if (currentLesson?.dynamicComponents) {
|
|
2201
|
-
|
|
2202
|
-
}
|
|
2210
|
+
// const currentLesson = this.lesson();
|
|
2211
|
+
// if (currentLesson?.dynamicComponents) {
|
|
2212
|
+
// return Object.values(currentLesson.dynamicComponents);
|
|
2213
|
+
// }
|
|
2203
2214
|
return []; // Return empty array if no lesson or no dynamic components
|
|
2204
|
-
});
|
|
2215
|
+
}, ...(ngDevMode ? [{ debugName: "dynamicComponentsArray" }] : []));
|
|
2205
2216
|
// States
|
|
2206
2217
|
this.components = {}; // Current Dynamic components
|
|
2207
2218
|
this.editor = BalloonEditor;
|
|
2208
2219
|
this.lessonComponentEnum = LessonComponentEnum;
|
|
2209
2220
|
this.coverStorageSettings = {
|
|
2210
|
-
path: `lessons/${this.
|
|
2221
|
+
path: `lessons/${this.entityId()}/covers`,
|
|
2211
2222
|
fileName: 'cover',
|
|
2212
2223
|
cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
|
|
2213
2224
|
};
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
}
|
|
2226
|
-
else {
|
|
2227
|
-
this.lesson.set(undefined); // Reset if not found
|
|
2228
|
-
this.#toastService.warn({ title: 'No se encontró la lección', subtitle: 'Quizá el id es incorrecto' });
|
|
2229
|
-
// Optional: Navigate away or show a specific "not found" state
|
|
2230
|
-
// this.#router.navigate(['/path/to/lessons']);
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
catch (error) {
|
|
2234
|
-
console.error('Error fetching lesson:', error);
|
|
2235
|
-
this.lesson.set(undefined); // Reset on error
|
|
2236
|
-
this.#toastService.error({ title: 'Error al cargar la lección', subtitle: 'Intenta de nuevo más tarde' });
|
|
2237
|
-
}
|
|
2238
|
-
finally {
|
|
2239
|
-
this.isLoadingLesson.set(false); // Stop loading
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
else {
|
|
2243
|
-
// Handle case for new lesson (ID is null/undefined)
|
|
2244
|
-
this.lesson.set({ textCoded: `<h1>Nueva lección </h1> <p> Texto aquí</p>`, tags: [] }); // Set default new lesson structure
|
|
2245
|
-
this.saveLesson();
|
|
2246
|
-
this.isLoadingLesson.set(false); // Ensure loading is off
|
|
2247
|
-
}
|
|
2248
|
-
});
|
|
2225
|
+
}
|
|
2226
|
+
patchForm(entity) {
|
|
2227
|
+
console.log(this.form);
|
|
2228
|
+
if (this.entity().format === 'markdown') {
|
|
2229
|
+
this.htmlTemporal = this.markdownService.parse(entity.markdown);
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
this.htmlTemporal = entity.textCoded;
|
|
2233
|
+
}
|
|
2234
|
+
// console.log(this.htmlTemporal);
|
|
2235
|
+
this.form.patchValue(entity);
|
|
2249
2236
|
}
|
|
2250
2237
|
/**
|
|
2251
2238
|
* Updates a specific property on the lesson signal.
|
|
@@ -2255,77 +2242,71 @@ class DCLessonEditorComponent {
|
|
|
2255
2242
|
*/
|
|
2256
2243
|
updateLessonProperty(property, value) {
|
|
2257
2244
|
console.log('Updating property:', property, value);
|
|
2258
|
-
this.
|
|
2245
|
+
this.entity.update((currentLesson) => {
|
|
2259
2246
|
if (!currentLesson)
|
|
2260
2247
|
return undefined;
|
|
2261
2248
|
return { ...currentLesson, [property]: value };
|
|
2262
2249
|
});
|
|
2263
2250
|
}
|
|
2264
|
-
|
|
2265
|
-
this.
|
|
2251
|
+
updateHtmlTextCoded(_, value) {
|
|
2252
|
+
this.entity.update((currentLesson) => {
|
|
2266
2253
|
if (!currentLesson)
|
|
2267
2254
|
return undefined;
|
|
2268
|
-
|
|
2269
|
-
return { ...currentLesson, tags: updatedTags };
|
|
2255
|
+
return { ...currentLesson, textCoded: value };
|
|
2270
2256
|
});
|
|
2257
|
+
this.form.controls.textCoded.setValue(value);
|
|
2271
2258
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
return currentLesson;
|
|
2280
|
-
}
|
|
2281
|
-
const updatedTags = [...currentLesson.tags, tag.value];
|
|
2282
|
-
return { ...currentLesson, tags: updatedTags };
|
|
2283
|
-
});
|
|
2284
|
-
}
|
|
2285
|
-
tag.input.nativeElement.value = ''; // Clear input
|
|
2259
|
+
onAssetsChange(updatedAssets) {
|
|
2260
|
+
console.log(updatedAssets);
|
|
2261
|
+
this.entity.update((currentLesson) => {
|
|
2262
|
+
if (!currentLesson)
|
|
2263
|
+
return undefined;
|
|
2264
|
+
return { ...currentLesson, assets: updatedAssets };
|
|
2265
|
+
});
|
|
2286
2266
|
}
|
|
2287
2267
|
async saveLesson(event) {
|
|
2268
|
+
// TODO: Revisar por ahora uso el método del padre para guardar. pero creo que este era para guardar la lección.
|
|
2288
2269
|
event?.preventDefault();
|
|
2289
|
-
const currentLesson = this.
|
|
2270
|
+
const currentLesson = this.entity();
|
|
2290
2271
|
if (!currentLesson) {
|
|
2291
|
-
this
|
|
2272
|
+
this.toastService.error({ title: 'Error', subtitle: 'No hay datos de lección para guardar' });
|
|
2292
2273
|
return undefined;
|
|
2293
2274
|
}
|
|
2294
2275
|
// Clean orphaned components before saving
|
|
2295
|
-
const lessonToSave = this
|
|
2276
|
+
const lessonToSave = this.lessonUtilsService.cleanOrphanedComponents(currentLesson);
|
|
2296
2277
|
// TODO: Implement optimization for saving only changed data.
|
|
2297
2278
|
// This requires comparing lessonToSave with the initially fetched state.
|
|
2298
2279
|
this.isLoadingLesson.set(true); // Indicate saving
|
|
2299
2280
|
try {
|
|
2300
2281
|
// Use the cleaned lesson object for saving
|
|
2301
|
-
const savedLesson = await this
|
|
2302
|
-
const currentId = this.
|
|
2282
|
+
const savedLesson = await this.lessonsService.postLesson(lessonToSave);
|
|
2283
|
+
const currentId = this.entityId();
|
|
2303
2284
|
if (!currentId) {
|
|
2304
2285
|
// No se como guardar los extras aunt
|
|
2305
2286
|
// It was a new lesson, now it has an ID. Navigate.
|
|
2306
|
-
this
|
|
2287
|
+
this.toastService.success({ title: 'Se creó la lección', subtitle: 'Éxito' });
|
|
2307
2288
|
// The effect should automatically fetch the lesson again after navigation due to paramMap change.
|
|
2308
|
-
this
|
|
2289
|
+
this.router.navigate(['./', savedLesson.id], { relativeTo: this.activatedRoute });
|
|
2309
2290
|
}
|
|
2310
2291
|
else {
|
|
2311
2292
|
// It was an existing lesson, update the signal with the potentially updated data from the backend.
|
|
2312
|
-
this.
|
|
2313
|
-
this
|
|
2293
|
+
this.entity.set(savedLesson);
|
|
2294
|
+
this.toastService.info({ title: 'Se guardaron los cambios en la lección', subtitle: 'Guardado' });
|
|
2314
2295
|
// Call the service method for validation
|
|
2315
|
-
this
|
|
2296
|
+
this.lessonUtilsService.validateAudios(this.entity());
|
|
2316
2297
|
}
|
|
2317
2298
|
return savedLesson;
|
|
2318
2299
|
}
|
|
2319
2300
|
catch (error) {
|
|
2320
2301
|
// Type error
|
|
2321
2302
|
console.error('Error saving lesson:', error);
|
|
2322
|
-
this
|
|
2303
|
+
this.toastService.error({ title: 'Error al guardar', subtitle: 'No se pudieron guardar los cambios' });
|
|
2323
2304
|
return undefined;
|
|
2324
2305
|
}
|
|
2325
2306
|
finally {
|
|
2326
2307
|
this.isLoadingLesson.set(false); // Finish saving indication
|
|
2327
2308
|
}
|
|
2328
|
-
}
|
|
2309
|
+
}
|
|
2329
2310
|
// Removed openComponentBuilder method
|
|
2330
2311
|
/**
|
|
2331
2312
|
* Handles the event emitted when a component is added via the adder component.
|
|
@@ -2337,7 +2318,7 @@ class DCLessonEditorComponent {
|
|
|
2337
2318
|
if (newComponent?.id) {
|
|
2338
2319
|
console.log('Component builder closed, result received in editor:', newComponent);
|
|
2339
2320
|
// Update the lesson signal, adding the transformed component to the dynamicComponents object
|
|
2340
|
-
this.
|
|
2321
|
+
this.entity.update((currentLesson) => {
|
|
2341
2322
|
if (!currentLesson)
|
|
2342
2323
|
return undefined;
|
|
2343
2324
|
// Ensure dynamicComponents object exists, initialize if not
|
|
@@ -2350,54 +2331,17 @@ class DCLessonEditorComponent {
|
|
|
2350
2331
|
// Return the updated lesson state
|
|
2351
2332
|
return { ...currentLesson, dynamicComponents: updatedDynamicComponents };
|
|
2352
2333
|
});
|
|
2353
|
-
// Optionally save the lesson after adding the component
|
|
2354
|
-
// this.saveLesson();
|
|
2355
2334
|
}
|
|
2356
2335
|
}
|
|
2357
|
-
// isLoadingLesson signal is used directly
|
|
2358
|
-
// Removed generateByAI and _extractTextFromEncodedJson methods.
|
|
2359
|
-
// This logic is now handled within DCLessonMetadataEditorComponent.
|
|
2360
|
-
/**
|
|
2361
|
-
* Handles the image upload event, updates the lesson signal via the service, and saves.
|
|
2362
|
-
* @param event The image upload event data.
|
|
2363
|
-
*/
|
|
2364
|
-
async onImageUploaded(event) {
|
|
2365
|
-
this.#lessonUtilsService.uploadCover(this.lesson, event);
|
|
2366
|
-
await this.saveLesson();
|
|
2367
|
-
}
|
|
2368
|
-
/**
|
|
2369
|
-
* Imports lesson content from Notion using the LessonNotionService.
|
|
2370
|
-
*/
|
|
2371
|
-
async importFromNotion() {
|
|
2372
|
-
// Use the service's loading state or manage locally
|
|
2373
|
-
this.isLoadingLesson.set(true);
|
|
2374
|
-
try {
|
|
2375
|
-
const newContent = await this.#lessonNotionService.importAndLinkLessonFromNotion(this.lesson(), this.lessonId());
|
|
2376
|
-
if (newContent !== null) {
|
|
2377
|
-
// Update the lesson signal's textCoded property
|
|
2378
|
-
this.updateLessonProperty('textCoded', newContent);
|
|
2379
|
-
// Toast success is handled within the service now
|
|
2380
|
-
}
|
|
2381
|
-
// If newContent is null, the service handled errors/toasts
|
|
2382
|
-
}
|
|
2383
|
-
finally {
|
|
2384
|
-
// Ensure loading state is reset regardless of service outcome
|
|
2385
|
-
// If observing service state: this.isLoadingLesson.set(this.#lessonNotionService.isLoading());
|
|
2386
|
-
this.isLoadingLesson.set(false); // Keep local loading for now
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
/**
|
|
2390
|
-
* Calls the LessonNotionService to improve the lesson using AI based on Notion content.
|
|
2391
|
-
*/
|
|
2392
2336
|
async improveNotionWithAI() {
|
|
2393
|
-
await this
|
|
2337
|
+
await this.lessonNotionService.improveLessonWithNotionAI(this.entity());
|
|
2394
2338
|
}
|
|
2395
2339
|
showComponentDetails(data) {
|
|
2396
2340
|
alert('showComponentDetails' + JSON.stringify(data));
|
|
2397
2341
|
}
|
|
2398
2342
|
async generateBanner() {
|
|
2399
|
-
this
|
|
2400
|
-
const prompt = this
|
|
2343
|
+
this.toastService.info({ title: 'Generando prompt de sugerencia', subtitle: 'Por favor, espera' });
|
|
2344
|
+
const prompt = this.lessonsService.getPrompts().banner(this.entity());
|
|
2401
2345
|
const geminiRes = await this.ngxVertexService.generateText([{ role: ChatRoleVertex.User, content: prompt }]);
|
|
2402
2346
|
this.promptService
|
|
2403
2347
|
.openPrompt({
|
|
@@ -2409,31 +2353,16 @@ class DCLessonEditorComponent {
|
|
|
2409
2353
|
})
|
|
2410
2354
|
.then((promptResult) => {
|
|
2411
2355
|
if (promptResult) {
|
|
2412
|
-
this
|
|
2413
|
-
this.defaultLessonsService.generateBanner(promptResult, this.
|
|
2356
|
+
this.loadingBarService.showIndeterminate();
|
|
2357
|
+
this.defaultLessonsService.generateBanner(promptResult, this.entityId()).then((result) => {
|
|
2414
2358
|
if (result) {
|
|
2415
|
-
|
|
2359
|
+
alert('Revisar como actualizar el banner');
|
|
2360
|
+
// this.updateLessonProperty('banner', (result as any).banner);
|
|
2416
2361
|
}
|
|
2417
|
-
this
|
|
2362
|
+
this.loadingBarService.successAndHide();
|
|
2418
2363
|
});
|
|
2419
2364
|
}
|
|
2420
2365
|
});
|
|
2421
|
-
// const imagePrompt = prompt('alguna idea de lo que quieres ver?');
|
|
2422
|
-
// this.#loadingBarService.showIndeterminate();
|
|
2423
|
-
// const result = await this.defaultLessonsService.generateBanner(imagePrompt, this.lessonId());
|
|
2424
|
-
// if (result) {
|
|
2425
|
-
// this.updateLessonProperty('banner', result.banner);
|
|
2426
|
-
// }
|
|
2427
|
-
// console.log('Generated banner:', result);
|
|
2428
|
-
//
|
|
2429
|
-
// this.#loadingBarService.successAndHide();
|
|
2430
|
-
}
|
|
2431
|
-
showPrompts() {
|
|
2432
|
-
this.promptsVisible = true;
|
|
2433
|
-
const promptsFn = this.#lessonService.getPrompts();
|
|
2434
|
-
this.prompts = { banner: promptsFn.banner(this.lesson()), content: promptsFn.content(this.lesson()), description: promptsFn.description(this.lesson()) };
|
|
2435
|
-
console.log(this.prompts);
|
|
2436
|
-
this.cdr.markForCheck();
|
|
2437
2366
|
}
|
|
2438
2367
|
editComponent(comp) {
|
|
2439
2368
|
console.log('Edit component:', comp);
|
|
@@ -2441,43 +2370,61 @@ class DCLessonEditorComponent {
|
|
|
2441
2370
|
this.onComponentAdded(result);
|
|
2442
2371
|
});
|
|
2443
2372
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2373
|
+
onAssetUpdate(event) {
|
|
2374
|
+
this.entityCommunicationService.partialUpdate(this.entityId(), { assets: event.assets });
|
|
2375
|
+
}
|
|
2376
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2377
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], usesInheritance: true, ngImport: i0, template: "<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader\n [assets]=\"entity()?.assets\"\n storagePath=\"lessons/{{ entityId() }}\"\n (assetsChange)=\"onAssetsChange($event)\"\n (assetUpdate)=\"onAssetUpdate($event)\"></assets-loader>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"entity()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"entity()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-form [form]=\"form.controls.manageable\"></dc-manageable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-form [form]=\"form.controls.learnable\"></dc-learnable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"entity()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"entity()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<div [formGroup]=\"form\">\n <p-selectButton [options]=\"formatOptions\" formControlName=\"format\" optionLabel=\"label\" optionValue=\"value\" />\n</div>\n<dc-lesson-metadata-editor [lesson]=\"entity()\" [form]=\"form\" [isLoadingLesson]=\"isLoadingLesson()\"></dc-lesson-metadata-editor>\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"htmlTemporal\"\n (ngModelChange)=\"updateHtmlTextCoded('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"entity()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"save()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<!-- <p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog> -->\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}::ng-deep .p-splitter .p-splitterpanel{overflow:auto!important}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i3$1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lessonInput", "lessonIdInput", "settings"], outputs: ["wordClicked"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i5$1.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i6$1.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "ngmodule", type: // Add the component adder here
|
|
2378
|
+
DialogModule }, { kind: "component", type: DcExtensionsViewerComponent, selector: "dc-extensions-viewer", inputs: ["data"] }, { kind: "component", type: DcLearnableViewerComponent, selector: "dc-learnable-viewer", inputs: ["data"] }, { kind: "component", type: DcAuditableViewerComponent, selector: "dc-auditable-viewer", inputs: ["data"] }, { kind: "component", type: DcReactionsViewerComponent, selector: "dc-reactions-viewer", inputs: ["data"] }, { kind: "component", type: AssetsLoaderComponent, selector: "assets-loader", inputs: ["assets", "storagePath"], outputs: ["assetsChange", "assetUpdate", "onFileSelected"] }, { kind: "component", type: DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["form", "lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest"] }, { kind: "component", type: DcManageableFormComponent, selector: "dc-manageable-form", inputs: ["form"] }, { kind: "component", type: DcLearnableFormComponent, selector: "dc-learnable-form", inputs: ["form"] }] }); }
|
|
2448
2379
|
}
|
|
2449
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
|
|
2450
2381
|
type: Component,
|
|
2451
2382
|
args: [{ selector: 'dc-lesson-editor', standalone: true, imports: [
|
|
2452
2383
|
ButtonModule,
|
|
2453
2384
|
CKEditorModule,
|
|
2454
|
-
CropperComponentModal,
|
|
2455
2385
|
DCLessonRendererComponent,
|
|
2456
2386
|
FormsModule,
|
|
2457
2387
|
InputTextModule,
|
|
2388
|
+
ReactiveFormsModule,
|
|
2389
|
+
SelectButtonModule,
|
|
2458
2390
|
SplitterModule,
|
|
2459
2391
|
TooltipModule,
|
|
2460
2392
|
DCLessonComponentAdderComponent, // Add the component adder here
|
|
2461
|
-
DCLessonMetadataEditorComponent, // Add the metadata editor here
|
|
2462
2393
|
DialogModule,
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2394
|
+
DcExtensionsViewerComponent,
|
|
2395
|
+
DcLearnableViewerComponent,
|
|
2396
|
+
DcAuditableViewerComponent,
|
|
2397
|
+
DcReactionsViewerComponent,
|
|
2398
|
+
AssetsLoaderComponent,
|
|
2399
|
+
DCLessonMetadataEditorComponent,
|
|
2400
|
+
DcManageableFormComponent,
|
|
2401
|
+
DcLearnableFormComponent,
|
|
2402
|
+
], providers: [LessonNotionService], template: "<div class=\"p-grid\">\n <div class=\"p-col-4\">\n <assets-loader\n [assets]=\"entity()?.assets\"\n storagePath=\"lessons/{{ entityId() }}\"\n (assetsChange)=\"onAssetsChange($event)\"\n (assetUpdate)=\"onAssetUpdate($event)\"></assets-loader>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-viewer [data]=\"entity()?.learnable\"></dc-learnable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Auditable</h3>\n <dc-auditable-viewer [data]=\"entity()?.auditable\"></dc-auditable-viewer>\n </div>\n <div class=\"p-col-4\">\n <h3>Manageable</h3>\n <dc-manageable-form [form]=\"form.controls.manageable\"></dc-manageable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Learnable</h3>\n <dc-learnable-form [form]=\"form.controls.learnable\"></dc-learnable-form>\n </div>\n <div class=\"p-col-4\">\n <h3>Reactions</h3>\n <dc-reactions-viewer [data]=\"entity()?.reactions\"></dc-reactions-viewer>\n </div>\n\n <div class=\"p-col-4\">\n <h3>Extensions</h3>\n <dc-extensions-viewer [data]=\"entity()?.extensions\"></dc-extensions-viewer>\n </div>\n</div>\n\n<!-- Lesson Metadata Editor -->\n<div [formGroup]=\"form\">\n <p-selectButton [options]=\"formatOptions\" formControlName=\"format\" optionLabel=\"label\" optionValue=\"value\" />\n</div>\n<dc-lesson-metadata-editor [lesson]=\"entity()\" [form]=\"form\" [isLoadingLesson]=\"isLoadingLesson()\"></dc-lesson-metadata-editor>\n\n<div style=\"margin-top: 30px\"></div>\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li\n >ID: {{ comp.id }} - Tipo: {{ comp.component }}\n\n <button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button>\n <p-button icon=\"pi pi-pencil\" [rounded]=\"true\" severity=\"warn\" (click)=\"editComponent(comp)\"></p-button>\n </li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"htmlTemporal\"\n (ngModelChange)=\"updateHtmlTextCoded('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"entity()\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"save()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n\n<!-- <p-dialog header=\"Prompts\" [modal]=\"true\" [(visible)]=\"promptsVisible\" [style]=\"{ width: '70%' }\">\n <div>\n <h1>Banner</h1>\n <p>{{ prompts?.banner }}</p>\n <h1>Contenido</h1>\n <p>{{ prompts?.content }}</p>\n <h1>Descripci\u00F3n</h1>\n <p>{{ prompts?.description }}</p>\n </div>\n</p-dialog> -->\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.generate-banner-btn{position:absolute;right:10px;top:10px}.prompt-visual{position:absolute;left:10px;bottom:10px}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;object-fit:cover;position:relative;border-radius:8px}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}::ng-deep .p-splitter .p-splitterpanel{overflow:auto!important}\n"] }]
|
|
2403
|
+
}] });
|
|
2404
|
+
|
|
2405
|
+
class LessonsV2Component {
|
|
2406
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonsV2Component, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2407
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: LessonsV2Component, isStandalone: true, selector: "app-lessonsv2", ngImport: i0, template: '<router-outlet></router-outlet>', isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
|
|
2408
|
+
}
|
|
2409
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonsV2Component, decorators: [{
|
|
2410
|
+
type: Component,
|
|
2411
|
+
args: [{
|
|
2412
|
+
selector: 'app-lessonsv2',
|
|
2413
|
+
template: '<router-outlet></router-outlet>',
|
|
2414
|
+
standalone: true,
|
|
2415
|
+
imports: [RouterOutlet],
|
|
2416
|
+
}]
|
|
2417
|
+
}] });
|
|
2471
2418
|
|
|
2472
2419
|
// This is the base class for all the components that are going to be used in the lessons.
|
|
2473
2420
|
class LessonDynamicComponent {
|
|
2474
2421
|
constructor() {
|
|
2475
2422
|
this.settings = {};
|
|
2476
2423
|
}
|
|
2477
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2478
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
2424
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonDynamicComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2425
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: LessonDynamicComponent, isStandalone: true, selector: "app-lesson-component", inputs: { settings: "settings" }, ngImport: i0, template: '<div>no template</div>', isInline: true }); }
|
|
2479
2426
|
}
|
|
2480
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2427
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: LessonDynamicComponent, decorators: [{
|
|
2481
2428
|
type: Component,
|
|
2482
2429
|
args: [{
|
|
2483
2430
|
selector: 'app-lesson-component',
|
|
@@ -2488,7 +2435,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2488
2435
|
type: Input
|
|
2489
2436
|
}] } });
|
|
2490
2437
|
|
|
2491
|
-
const Endpoints
|
|
2438
|
+
const Endpoints = {
|
|
2492
2439
|
courses: 'api/courses',
|
|
2493
2440
|
};
|
|
2494
2441
|
class CoursesService {
|
|
@@ -2497,12 +2444,12 @@ class CoursesService {
|
|
|
2497
2444
|
}
|
|
2498
2445
|
// Not sure how to implement this yet.
|
|
2499
2446
|
getCourses() {
|
|
2500
|
-
return this.httpCoreService.get(Endpoints
|
|
2447
|
+
return this.httpCoreService.get(Endpoints.courses);
|
|
2501
2448
|
}
|
|
2502
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2503
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2449
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2450
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesService, providedIn: 'root' }); }
|
|
2504
2451
|
}
|
|
2505
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesService, decorators: [{
|
|
2506
2453
|
type: Injectable,
|
|
2507
2454
|
args: [{
|
|
2508
2455
|
providedIn: 'root',
|
|
@@ -2517,92 +2464,48 @@ class CoursesAdminComponent {
|
|
|
2517
2464
|
const courses = await this.httpCoreService.get('courses');
|
|
2518
2465
|
console.log(courses);
|
|
2519
2466
|
}
|
|
2520
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2521
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
2467
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesAdminComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2468
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CoursesAdminComponent, isStandalone: true, selector: "ngx-courses-admin", ngImport: i0, template: "<p>welcome to courses creation</p>\n", styles: [""] }); }
|
|
2522
2469
|
}
|
|
2523
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2470
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesAdminComponent, decorators: [{
|
|
2524
2471
|
type: Component,
|
|
2525
2472
|
args: [{ selector: 'ngx-courses-admin', standalone: true, template: "<p>welcome to courses creation</p>\n" }]
|
|
2526
2473
|
}] });
|
|
2527
2474
|
|
|
2528
|
-
class
|
|
2529
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoursesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2530
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: CoursesComponent, isStandalone: true, selector: "app-courses", ngImport: i0, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2531
|
-
}
|
|
2532
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CoursesComponent, decorators: [{
|
|
2533
|
-
type: Component,
|
|
2534
|
-
args: [{ selector: 'app-courses', imports: [RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"] }]
|
|
2535
|
-
}] });
|
|
2536
|
-
|
|
2537
|
-
class CourseDetailComponent {
|
|
2538
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CourseDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2539
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: CourseDetailComponent, isStandalone: true, selector: "app-course-detail", ngImport: i0, template: `<p>course-detail works!</p>`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2540
|
-
}
|
|
2541
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: CourseDetailComponent, decorators: [{
|
|
2542
|
-
type: Component,
|
|
2543
|
-
args: [{ selector: 'app-course-detail', imports: [], template: `<p>course-detail works!</p>`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
|
|
2544
|
-
}] });
|
|
2545
|
-
|
|
2546
|
-
const server = 'primary';
|
|
2547
|
-
// TODO add your own end points
|
|
2548
|
-
const Endpoints = {
|
|
2549
|
-
Courses: {
|
|
2550
|
-
Courses: 'api/courses',
|
|
2551
|
-
CoursesFiltered: 'api/courses/query',
|
|
2552
|
-
},
|
|
2553
|
-
};
|
|
2554
|
-
class CourseService {
|
|
2475
|
+
class CourseService extends EntityCommunicationService {
|
|
2555
2476
|
constructor() {
|
|
2556
|
-
|
|
2557
|
-
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
2558
|
-
}
|
|
2559
|
-
async getCourses() {
|
|
2560
|
-
try {
|
|
2561
|
-
const response = await this.httpService.get(Endpoints.Courses.Courses, server);
|
|
2562
|
-
this.toastService.success({ title: 'Se han encontrado generics', subtitle: 'Mostrando información' });
|
|
2563
|
-
return response;
|
|
2564
|
-
}
|
|
2565
|
-
catch (error) {
|
|
2566
|
-
this.toastService.warn({ title: 'Error fetching generics', subtitle: 'Showing Default Data' });
|
|
2567
|
-
// return RemoveSimpleDataExample;
|
|
2568
|
-
return [];
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
async getFilteredCourses(filter) {
|
|
2572
|
-
return this.httpService.post(Endpoints.Courses.CoursesFiltered, filter, server);
|
|
2573
|
-
}
|
|
2574
|
-
async getCourse(id) {
|
|
2575
|
-
return this.httpService.get(`${Endpoints.Courses.Courses}/${id}`);
|
|
2477
|
+
super('courses');
|
|
2576
2478
|
}
|
|
2577
|
-
|
|
2578
|
-
return this.httpService.post(
|
|
2479
|
+
generateLanguageCourse(base, target, id = '') {
|
|
2480
|
+
return this.httpService.post(`api/courses/generate-language`, { base, target, id });
|
|
2579
2481
|
}
|
|
2580
|
-
|
|
2581
|
-
return this.httpService.
|
|
2482
|
+
autogenerateLessons(id) {
|
|
2483
|
+
return this.httpService.post(`api/courses/autogenerate-lessons`, { id });
|
|
2582
2484
|
}
|
|
2583
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2584
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2485
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2486
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseService, providedIn: 'root' }); }
|
|
2585
2487
|
}
|
|
2586
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2488
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseService, decorators: [{
|
|
2587
2489
|
type: Injectable,
|
|
2588
2490
|
args: [{
|
|
2589
2491
|
providedIn: 'root',
|
|
2590
2492
|
}]
|
|
2591
|
-
}] });
|
|
2493
|
+
}], ctorParameters: () => [] });
|
|
2592
2494
|
|
|
2593
2495
|
class CourseListComponent extends PaginationBase {
|
|
2594
2496
|
constructor() {
|
|
2595
2497
|
super(...arguments);
|
|
2596
2498
|
// Services
|
|
2597
2499
|
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
2598
|
-
this.
|
|
2500
|
+
this.courseService = inject(CourseService);
|
|
2501
|
+
this.userService = inject(UserService);
|
|
2599
2502
|
this.cdr = inject(ChangeDetectorRef);
|
|
2600
2503
|
// Inputs
|
|
2601
2504
|
this.viewType = 'card';
|
|
2602
|
-
this.onlyView = input(true);
|
|
2505
|
+
this.onlyView = input(true, ...(ngDevMode ? [{ debugName: "onlyView" }] : []));
|
|
2603
2506
|
this.onSelect = output();
|
|
2604
2507
|
// States
|
|
2605
|
-
this.courses = signal([]);
|
|
2508
|
+
this.courses = signal([], ...(ngDevMode ? [{ debugName: "courses" }] : []));
|
|
2606
2509
|
this.columns = ['name', 'description', 'updatedAt', 'image'];
|
|
2607
2510
|
this.filterBarOptions = { showActions: true, showCreateButton: true, showViewButton: true };
|
|
2608
2511
|
}
|
|
@@ -2626,8 +2529,14 @@ class CourseListComponent extends PaginationBase {
|
|
|
2626
2529
|
];
|
|
2627
2530
|
}
|
|
2628
2531
|
async ngOnInit() {
|
|
2629
|
-
this.
|
|
2630
|
-
const
|
|
2532
|
+
const user = this.userService.user();
|
|
2533
|
+
const targetLang = user?.settings?.['targetLanguage'];
|
|
2534
|
+
const baseLang = user?.settings?.['baseLanguage'];
|
|
2535
|
+
// console.log(user?.settings?.['targetLanguage']);
|
|
2536
|
+
this.filterConfig.filters = { baseLang: baseLang, targetLang: targetLang };
|
|
2537
|
+
this.filterConfig.returnProps = { _id: 1, id: 1, name: 1, description: 1, updatedAt: 1, image: 1, baseLang: 1, targetLang: 1, auditable: 1 };
|
|
2538
|
+
// this.filterConfig.filters = { targetLang: 'de', baseLang: 'es' };
|
|
2539
|
+
const response = await this.courseService.query(this.filterConfig);
|
|
2631
2540
|
this.courses.set(response.rows);
|
|
2632
2541
|
this.cdr.detectChanges();
|
|
2633
2542
|
console.log(this.courses(), this.viewType);
|
|
@@ -2654,15 +2563,15 @@ class CourseListComponent extends PaginationBase {
|
|
|
2654
2563
|
if (action == 'changeView') {
|
|
2655
2564
|
this.toggleView();
|
|
2656
2565
|
}
|
|
2566
|
+
const id = item.id || item._id;
|
|
2657
2567
|
switch (action) {
|
|
2658
2568
|
case 'view':
|
|
2659
|
-
this.router.navigate(['./details',
|
|
2569
|
+
this.router.navigate(['./details', id], { relativeTo: this.route });
|
|
2660
2570
|
break;
|
|
2661
2571
|
case 'delete':
|
|
2662
2572
|
const areYouSure = confirm('¿Estás seguro de querer eliminar este origen?');
|
|
2663
2573
|
if (areYouSure) {
|
|
2664
|
-
|
|
2665
|
-
await this.sourceService.deleteCourse(id);
|
|
2574
|
+
await this.courseService.remove(id);
|
|
2666
2575
|
this.courses.set(this.courses().filter((course) => course._id !== id));
|
|
2667
2576
|
this.toastService.success({
|
|
2668
2577
|
title: 'Origen eliminado',
|
|
@@ -2672,14 +2581,14 @@ class CourseListComponent extends PaginationBase {
|
|
|
2672
2581
|
}
|
|
2673
2582
|
break;
|
|
2674
2583
|
case 'edit':
|
|
2675
|
-
this.router.navigate(['./edit',
|
|
2584
|
+
this.router.navigate(['./edit', id], { relativeTo: this.route });
|
|
2676
2585
|
break;
|
|
2677
2586
|
}
|
|
2678
2587
|
}
|
|
2679
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2680
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
2588
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseListComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2589
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseListComponent, isStandalone: true, selector: "app-course-list", inputs: { viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: false, isRequired: false, transformFunction: null }, onlyView: { classPropertyName: "onlyView", publicName: "onlyView", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, usesInheritance: true, ngImport: i0, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course?.auditable?.createdAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n\n <p-tag severity=\"success\" [value]=\"course.baseLang | langDesc : 'es'\" [rounded]=\"true\" />\n ->\n <p-tag severity=\"info\" [value]=\"course.targetLang | langDesc : 'es'\" [rounded]=\"true\" />\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin:20px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin:20px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"], dependencies: [{ kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["items", "options", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$1.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: LangDescTranslation, name: "langDesc" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2681
2590
|
}
|
|
2682
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2591
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseListComponent, decorators: [{
|
|
2683
2592
|
type: Component,
|
|
2684
2593
|
args: [{ selector: 'app-course-list', imports: [
|
|
2685
2594
|
CardModule,
|
|
@@ -2692,19 +2601,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2692
2601
|
RouterModule,
|
|
2693
2602
|
TableModule,
|
|
2694
2603
|
QuickTableComponent,
|
|
2695
|
-
|
|
2604
|
+
TagModule,
|
|
2605
|
+
LangDescTranslation,
|
|
2606
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!onlyView()) {\n<p-button [icon]=\"viewType === 'card' ? 'pi pi-table' : 'pi pi-list'\" label=\"Change View\" [link]=\"true\" (click)=\"toggleView()\" />\n}\n<div class=\"course-list-container\">\n <dc-filter-bar [options]=\"filterBarOptions\" (onNew)=\"onNew()\" (onFilterAction)=\"doAction($event)\"></dc-filter-bar>\n\n @if (viewType === 'card') {\n <div class=\"course-list-content\">\n @for (course of courses(); track course.id) {\n <div class=\"card-source\">\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(course)\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true }\"\n [tooltipOptions]=\"{ tooltipPosition: 'top' }\" />\n </div>\n <p-card [header]=\"course.name\">\n <p class=\"m-0\">{{ course.description | slice : 0 : 250 }}...</p>\n <span>{{ course?.auditable?.createdAt | date : 'dd/MM/yyyy HH:mm' }}</span>\n\n <p-tag severity=\"success\" [value]=\"course.baseLang | langDesc : 'es'\" [rounded]=\"true\" />\n ->\n <p-tag severity=\"info\" [value]=\"course.targetLang | langDesc : 'es'\" [rounded]=\"true\" />\n </p-card>\n </div>\n } @if (courses().length === 0) {\n <p-card>\n <p>No courses found</p>\n </p-card>\n }\n </div>\n } @else if ( viewType == 'table'){\n\n <app-quick-table [tableData]=\"courses()\"></app-quick-table>\n\n }\n\n <div class=\"paginator-container\">\n <p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n </p-paginator>\n </div>\n</div>\n", styles: [":host{display:block;height:100%}.course-list-container{display:flex;flex-direction:column;height:100%}.course-list-content{margin:20px;flex:1;overflow-y:auto;padding-bottom:10px}.card-source{margin:20px;position:relative}.paginator-container{margin-top:auto;padding-top:10px}\n"] }]
|
|
2696
2607
|
}], propDecorators: { viewType: [{
|
|
2697
2608
|
type: Input
|
|
2698
2609
|
}] } });
|
|
2699
2610
|
|
|
2700
|
-
class
|
|
2611
|
+
class CourseDetailComponent {
|
|
2701
2612
|
constructor() {
|
|
2702
|
-
this.
|
|
2703
|
-
this.
|
|
2613
|
+
this.entityCommunicationService = inject(CourseService);
|
|
2614
|
+
this.activatedRoute = inject(ActivatedRoute);
|
|
2615
|
+
this.messageService = inject(MessageService);
|
|
2616
|
+
this.courseId = this.activatedRoute.snapshot.paramMap.get('id');
|
|
2617
|
+
this.course = signal(null, ...(ngDevMode ? [{ debugName: "course" }] : []));
|
|
2618
|
+
this.generatingLessons = signal(false, ...(ngDevMode ? [{ debugName: "generatingLessons" }] : []));
|
|
2619
|
+
this.generatingLesson = signal('', ...(ngDevMode ? [{ debugName: "generatingLesson" }] : []));
|
|
2620
|
+
}
|
|
2621
|
+
ngOnInit() {
|
|
2622
|
+
this.loadCourse();
|
|
2623
|
+
}
|
|
2624
|
+
async generatePendingLessons() {
|
|
2625
|
+
this.generatingLessons.set(true);
|
|
2626
|
+
try {
|
|
2627
|
+
await this.entityCommunicationService.autogenerateLessons(this.courseId);
|
|
2628
|
+
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Lessons generated successfully' });
|
|
2629
|
+
this.loadCourse();
|
|
2630
|
+
}
|
|
2631
|
+
catch (error) {
|
|
2632
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error generating lessons' });
|
|
2633
|
+
}
|
|
2634
|
+
finally {
|
|
2635
|
+
this.generatingLessons.set(false);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
async generateLesson(lessonId) {
|
|
2639
|
+
this.generatingLesson.set(lessonId);
|
|
2640
|
+
try {
|
|
2641
|
+
// TODO: Implement the generateLesson method
|
|
2642
|
+
// await this.entityCommunicationService.autogenerateLesson(this.courseId, lessonId);
|
|
2643
|
+
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Lesson generated successfully' });
|
|
2644
|
+
this.loadCourse();
|
|
2645
|
+
}
|
|
2646
|
+
catch (error) {
|
|
2647
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error generating lesson' });
|
|
2648
|
+
}
|
|
2649
|
+
finally {
|
|
2650
|
+
this.generatingLesson.set('');
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
async loadCourse() {
|
|
2654
|
+
const course = await this.entityCommunicationService.findOne(this.courseId);
|
|
2655
|
+
this.course.set(course);
|
|
2656
|
+
}
|
|
2657
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2658
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseDetailComponent, isStandalone: true, selector: "app-course-detail", providers: [MessageService], ngImport: i0, template: "@if (course(); as course) {\n<div class=\"course-detail-container p-4\">\n <p-card>\n <ng-template pTemplate=\"title\">\n <div class=\"flex justify-content-between align-items-center\">\n <span>{{ course.name }}</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"subtitle\">\n <div class=\"flex align-items-center gap-2\">\n <i class=\"pi pi-book\"></i>\n <span>{{ course.moduleCount }} Modules / {{ course.totalLessons }} Lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ course.description }}</p>\n\n <div class=\"grid mt-3\">\n @for (module of course.modules; track module.id) {\n <div class=\"col-12 md:col-6\">\n <p-panel [toggleable]=\"true\">\n <ng-template pTemplate=\"header\">\n <div class=\"flex align-items-center gap-2 w-full\">\n <i class=\"pi pi-list\"></i>\n <span class=\"font-bold white-space-nowrap\">{{ module.title }}</span>\n <span class=\"text-sm text-color-secondary ml-auto\">{{ module.lessonCount }} lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ module.description }}</p>\n <ul class=\"list-none p-0 m-0 lessons-list\">\n @for (lesson of module.lessons; track lesson.id) {\n <li class=\"flex align-items-center justify-content-between p-2 border-bottom-1 surface-border\">\n <div class=\"flex align-items-center gap-2\">\n @if(lesson.generated) {\n <i class=\"pi pi-play-circle text-green-500\"></i>\n } @else {\n <i class=\"pi pi-lock text-gray-500\"></i>\n }\n <span>{{ lesson.title }}</span>\n </div>\n @if(lesson.generated) {\n <p-button icon=\"pi pi-chevron-right\" [text]=\"true\" [rounded]=\"true\"></p-button>\n } @else {\n <p-button label=\"Generate\" icon=\"pi pi-cog\" [text]=\"true\" size=\"small\"></p-button>\n }\n </li>\n }\n </ul>\n </ng-template>\n </p-panel>\n </div>\n }\n </div>\n </ng-template>\n <ng-template pTemplate=\"footer\">\n <div class=\"flex justify-content-end\">\n <p-button label=\"Generate Pending Lessons\" icon=\"pi pi-cogs\" (onClick)=\"generatePendingLessons()\" [loading]=\"generatingLessons()\"></p-button>\n </div>\n </ng-template>\n </p-card>\n</div>\n} @else {\n<div class=\"flex justify-content-center align-items-center h-full\">\n <p-progressSpinner></p-progressSpinner>\n</div>\n}\n", styles: [":host{display:block;height:100%}.course-detail-container{max-width:960px;margin:auto}.lessons-list li:last-child{border-bottom:none!important}.modules-container{display:flex;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:1rem}.modules-container .p-panel{flex:0 0 48%;max-width:48%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i3$2.Panel, selector: "p-panel", inputs: ["toggleable", "header", "collapsed", "id", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i5$2.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: ToastModule }, { kind: "ngmodule", type: TagModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2659
|
+
}
|
|
2660
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseDetailComponent, decorators: [{
|
|
2661
|
+
type: Component,
|
|
2662
|
+
args: [{ selector: 'app-course-detail', imports: [CommonModule, CardModule, PanelModule, ButtonModule, ProgressSpinnerModule, ToastModule, TagModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [MessageService], template: "@if (course(); as course) {\n<div class=\"course-detail-container p-4\">\n <p-card>\n <ng-template pTemplate=\"title\">\n <div class=\"flex justify-content-between align-items-center\">\n <span>{{ course.name }}</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"subtitle\">\n <div class=\"flex align-items-center gap-2\">\n <i class=\"pi pi-book\"></i>\n <span>{{ course.moduleCount }} Modules / {{ course.totalLessons }} Lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ course.description }}</p>\n\n <div class=\"grid mt-3\">\n @for (module of course.modules; track module.id) {\n <div class=\"col-12 md:col-6\">\n <p-panel [toggleable]=\"true\">\n <ng-template pTemplate=\"header\">\n <div class=\"flex align-items-center gap-2 w-full\">\n <i class=\"pi pi-list\"></i>\n <span class=\"font-bold white-space-nowrap\">{{ module.title }}</span>\n <span class=\"text-sm text-color-secondary ml-auto\">{{ module.lessonCount }} lessons</span>\n </div>\n </ng-template>\n <ng-template pTemplate=\"content\">\n <p>{{ module.description }}</p>\n <ul class=\"list-none p-0 m-0 lessons-list\">\n @for (lesson of module.lessons; track lesson.id) {\n <li class=\"flex align-items-center justify-content-between p-2 border-bottom-1 surface-border\">\n <div class=\"flex align-items-center gap-2\">\n @if(lesson.generated) {\n <i class=\"pi pi-play-circle text-green-500\"></i>\n } @else {\n <i class=\"pi pi-lock text-gray-500\"></i>\n }\n <span>{{ lesson.title }}</span>\n </div>\n @if(lesson.generated) {\n <p-button icon=\"pi pi-chevron-right\" [text]=\"true\" [rounded]=\"true\"></p-button>\n } @else {\n <p-button label=\"Generate\" icon=\"pi pi-cog\" [text]=\"true\" size=\"small\"></p-button>\n }\n </li>\n }\n </ul>\n </ng-template>\n </p-panel>\n </div>\n }\n </div>\n </ng-template>\n <ng-template pTemplate=\"footer\">\n <div class=\"flex justify-content-end\">\n <p-button label=\"Generate Pending Lessons\" icon=\"pi pi-cogs\" (onClick)=\"generatePendingLessons()\" [loading]=\"generatingLessons()\"></p-button>\n </div>\n </ng-template>\n </p-card>\n</div>\n} @else {\n<div class=\"flex justify-content-center align-items-center h-full\">\n <p-progressSpinner></p-progressSpinner>\n</div>\n}\n", styles: [":host{display:block;height:100%}.course-detail-container{max-width:960px;margin:auto}.lessons-list li:last-child{border-bottom:none!important}.modules-container{display:flex;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:1rem}.modules-container .p-panel{flex:0 0 48%;max-width:48%}\n"] }]
|
|
2663
|
+
}] });
|
|
2664
|
+
|
|
2665
|
+
class CourseFormComponent extends EntityBaseFormComponent {
|
|
2666
|
+
constructor() {
|
|
2667
|
+
super();
|
|
2668
|
+
this.entityCommunicationService = inject(CourseService);
|
|
2669
|
+
this.loadingBarService = inject(LoadingBarService);
|
|
2704
2670
|
this.fb = inject(FormBuilder);
|
|
2705
|
-
this.
|
|
2706
|
-
this.
|
|
2707
|
-
|
|
2671
|
+
this.languageOptions = getSupportedLanguageOptions('en');
|
|
2672
|
+
this.form = this.fb.group({
|
|
2673
|
+
name: ['', Validators.required],
|
|
2674
|
+
description: [''],
|
|
2675
|
+
image: [{}],
|
|
2676
|
+
targetLang: ['', Validators.required],
|
|
2677
|
+
baseLang: ['', Validators.required],
|
|
2678
|
+
});
|
|
2679
|
+
this.onSave = output();
|
|
2708
2680
|
this.storageImgSettings = {
|
|
2709
2681
|
path: `courses`,
|
|
2710
2682
|
cropSettings: { aspectRatio: AspectType.Square, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 700 },
|
|
@@ -2713,46 +2685,49 @@ class CourseFormComponent {
|
|
|
2713
2685
|
{ key: 'title', type: 'input', props: { label: 'Title', placeholder: 'Title', required: false } },
|
|
2714
2686
|
{ key: 'content', type: 'textarea', props: { label: 'Content', placeholder: 'Content', required: false } },
|
|
2715
2687
|
];
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
description: [''],
|
|
2719
|
-
image: [{}],
|
|
2688
|
+
effect(() => {
|
|
2689
|
+
console.log(this.entity());
|
|
2720
2690
|
});
|
|
2721
|
-
this.course = null;
|
|
2722
|
-
this.courseId = this.route.snapshot.params['id'];
|
|
2723
2691
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
this.course = await this.courseService.getCourse(this.courseId);
|
|
2727
|
-
if (this.course) {
|
|
2728
|
-
this.courseForm.patchValue(this.course);
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2692
|
+
patchForm(entity) {
|
|
2693
|
+
this.form.patchValue(entity);
|
|
2731
2694
|
}
|
|
2732
2695
|
async save() {
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2696
|
+
this.toastService.success({ title: 'Course', subtitle: 'Data was saved' });
|
|
2697
|
+
const result = await super.save();
|
|
2698
|
+
if (result) {
|
|
2699
|
+
this.onSave.emit(result);
|
|
2700
|
+
if (this.toastService) {
|
|
2701
|
+
this.toastService.success({ title: 'Course', subtitle: 'Data was saved' });
|
|
2738
2702
|
}
|
|
2739
|
-
this.toastService.success({ title: 'Origen guardado', subtitle: 'El origen ha sido guardado correctamente' });
|
|
2740
2703
|
}
|
|
2704
|
+
return result;
|
|
2741
2705
|
}
|
|
2742
2706
|
handleImageUpload(event) {
|
|
2743
|
-
|
|
2744
|
-
|
|
2707
|
+
this.form.patchValue({ image: event });
|
|
2708
|
+
}
|
|
2709
|
+
async generateCourse() {
|
|
2710
|
+
const { baseLang, targetLang } = this.form.value;
|
|
2711
|
+
if (baseLang && targetLang) {
|
|
2712
|
+
this.toastService.info({ title: 'Course Generating', subtitle: 'Este proceso puede tomar unos minutos' });
|
|
2713
|
+
this.loadingBarService.showIndeterminate();
|
|
2714
|
+
const course = await this.entityCommunicationService.generateLanguageCourse(baseLang, targetLang, this.entity()?.id);
|
|
2715
|
+
this.form.patchValue(course);
|
|
2716
|
+
if (this.toastService) {
|
|
2717
|
+
this.toastService.success({ title: 'Course Generated', subtitle: 'Course content has been generated successfully.' });
|
|
2718
|
+
}
|
|
2719
|
+
this.loadingBarService.successAndHide();
|
|
2720
|
+
}
|
|
2745
2721
|
}
|
|
2746
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2747
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2722
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2723
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.6", type: CourseFormComponent, isStandalone: true, selector: "app-source-form", outputs: { onSave: "onSave" }, usesInheritance: true, ngImport: i0, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div>\n @if (entity()?.modules?.length > 0) {\n <p-message severity=\"info\"\n >El curso ya tiene modulos,\n\n <a routerLink=\"../../details/{{ entity()?.id }}\"> <p-button icon=\"pi pi-eye\" label=\"Ver a los detalles\" [link]=\"true\"></p-button></a>\n </p-message>\n } @else {\n <p-message severity=\"warn\">Este curso no tiene modulos intentega generalos abajo.</p-message>\n }\n </div>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button\n (click)=\"generateCourse()\"\n label=\"Generate Course\"\n [disabled]=\"!form.controls['baseLang'].valid || !form.controls['targetLang'].valid\"\n icon=\"pi pi-sparkles\"\n iconPos=\"right\"\n styleClass=\"p-button-secondary\"></p-button>\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3$3.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: FormlyModule }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i4.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2748
2724
|
}
|
|
2749
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2725
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CourseFormComponent, decorators: [{
|
|
2750
2726
|
type: Component,
|
|
2751
2727
|
args: [{ selector: 'app-source-form', imports: [
|
|
2752
2728
|
ReactiveFormsModule,
|
|
2753
2729
|
CardModule,
|
|
2754
2730
|
TextareaModule,
|
|
2755
|
-
DropdownModule,
|
|
2756
2731
|
ButtonModule,
|
|
2757
2732
|
SelectModule,
|
|
2758
2733
|
InputTextModule,
|
|
@@ -2762,7 +2737,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2762
2737
|
FormlyModule,
|
|
2763
2738
|
DialogModule,
|
|
2764
2739
|
CourseListComponent,
|
|
2765
|
-
|
|
2740
|
+
MessageModule,
|
|
2741
|
+
RouterLink,
|
|
2742
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<h3>Courses Form</h3>\n\n<div class=\"source-form-card\">\n <p-card [header]=\"entityId() ? 'Edit Course' : 'New Course'\">\n <form [formGroup]=\"form\">\n <div class=\"form-field\">\n <label for=\"baseLang\">Base Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"baseLang\"\n [options]=\"languageOptions\"\n [filter]=\"true\"\n formControlName=\"baseLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n <div class=\"form-field\">\n <label for=\"targetLang\">Target Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"targetLang\"\n [filter]=\"true\"\n [options]=\"languageOptions\"\n formControlName=\"targetLang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div style=\"display: flex; gap: 10px\">\n <div class=\"form-field\">\n <label class=\"block\" pTooltip=\"Image should be handle after upload\">Subir imagen</label>\n <img width=\"218px\" src=\"assets/images/face-3.jpg\" />\n <dc-cropper-modal [imgStorageSettings]=\"storageImgSettings\" (imageUploaded)=\"handleImageUpload($event)\"></dc-cropper-modal>\n </div>\n\n <div style=\"width: 100%\">\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Name</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter source name\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"description\" class=\"block\">Description</label>\n <textarea id=\"description\" pTextarea formControlName=\"description\" rows=\"5\" class=\"w-full\" placeholder=\"Enter source content\"> </textarea>\n </div>\n </div>\n </div>\n </form>\n\n <div>\n @if (entity()?.modules?.length > 0) {\n <p-message severity=\"info\"\n >El curso ya tiene modulos,\n\n <a routerLink=\"../../details/{{ entity()?.id }}\"> <p-button icon=\"pi pi-eye\" label=\"Ver a los detalles\" [link]=\"true\"></p-button></a>\n </p-message>\n } @else {\n <p-message severity=\"warn\">Este curso no tiene modulos intentega generalos abajo.</p-message>\n }\n </div>\n\n <div style=\"display: flex; justify-content: flex-end\">\n <p-button\n (click)=\"generateCourse()\"\n label=\"Generate Course\"\n [disabled]=\"!form.controls['baseLang'].valid || !form.controls['targetLang'].valid\"\n icon=\"pi pi-sparkles\"\n iconPos=\"right\"\n styleClass=\"p-button-secondary\"></p-button>\n <p-button (click)=\"save()\" label=\"Save Course\" [disabled]=\"!form.valid\" icon=\"pi pi-check\" iconPos=\"right\"> </p-button>\n </div>\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"] }]
|
|
2743
|
+
}], ctorParameters: () => [] });
|
|
2744
|
+
|
|
2745
|
+
const COURSES_ROUTES = [
|
|
2746
|
+
{
|
|
2747
|
+
path: '',
|
|
2748
|
+
component: CoursesComponent,
|
|
2749
|
+
children: [
|
|
2750
|
+
{
|
|
2751
|
+
path: '',
|
|
2752
|
+
component: CourseListComponent,
|
|
2753
|
+
},
|
|
2754
|
+
{
|
|
2755
|
+
path: 'details/:id',
|
|
2756
|
+
component: CourseDetailComponent,
|
|
2757
|
+
},
|
|
2758
|
+
{
|
|
2759
|
+
path: 'edit',
|
|
2760
|
+
component: CourseFormComponent,
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
path: 'edit/:id',
|
|
2764
|
+
component: CourseFormComponent,
|
|
2765
|
+
},
|
|
2766
|
+
],
|
|
2767
|
+
},
|
|
2768
|
+
];
|
|
2769
|
+
|
|
2770
|
+
class CoursesComponent {
|
|
2771
|
+
static { this.routes = COURSES_ROUTES; }
|
|
2772
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2773
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.6", type: CoursesComponent, isStandalone: true, selector: "app-courses", ngImport: i0, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$4.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2774
|
+
}
|
|
2775
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: CoursesComponent, decorators: [{
|
|
2776
|
+
type: Component,
|
|
2777
|
+
args: [{ selector: 'app-courses', imports: [RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<router-outlet />\n", styles: [":host{display:block;height:100%}\n"] }]
|
|
2766
2778
|
}] });
|
|
2767
2779
|
|
|
2768
2780
|
/*
|
|
@@ -2773,5 +2785,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2773
2785
|
* Generated bundle index. Do not edit.
|
|
2774
2786
|
*/
|
|
2775
2787
|
|
|
2776
|
-
export { ComponentBuilder, ComponentWithForm, CourseDetailComponent, CourseFormComponent, CourseListComponent, CourseService, CoursesAdminComponent, CoursesComponent, CoursesService, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, DefaultLessonsService, DynamicComponentBuilders, DynamicComponents, DynamicComponentsService, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs,
|
|
2788
|
+
export { ComponentBuilder, ComponentWithForm, CourseDetailComponent, CourseFormComponent, CourseListComponent, CourseService, CoursesAdminComponent, CoursesComponent, CoursesService, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, DefaultLessonsService, DynamicComponentBuilders, DynamicComponents, DynamicComponentsService, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonConversationService, LessonDynamicComponent, LessonRendererService, LessonsV2Component, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLanguageSimpleAgent, getLessonComponentClass, provideLessonsService, provideNotionService };
|
|
2777
2789
|
//# sourceMappingURL=dataclouder-ngx-lessons.mjs.map
|