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