@dataclouder/ngx-lessons 0.0.33 → 0.0.34
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 +373 -107
- package/fesm2022/dataclouder-ngx-lessons.mjs.map +1 -1
- package/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.d.ts +0 -7
- package/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.d.ts +19 -3
- package/lib/components/dc-lessons/dc-lesson-renderer/dc-lesson-renderer.component.d.ts +5 -1
- package/lib/components/dc-lessons/dc-lesson-renderer/eval-agents-skills.d.ts +1 -0
- package/lib/components/lesson-mini-components/components/lessons.clases.d.ts +8 -0
- package/lib/services/default-lessons.service.d.ts +18 -0
- package/lib/services/lesson-utils.service.d.ts +14 -2
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -20,9 +20,11 @@ import { PaginatorModule } from 'primeng/paginator';
|
|
|
20
20
|
import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule } from '@angular/common';
|
|
21
21
|
import * as i1$2 from '@angular/router';
|
|
22
22
|
import { RouterModule, ActivatedRoute, Router } from '@angular/router';
|
|
23
|
-
import * as i4$
|
|
24
|
-
import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService } from '@dataclouder/ngx-core';
|
|
23
|
+
import * as i4$2 from '@dataclouder/ngx-core';
|
|
24
|
+
import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService, HttpCoreService } from '@dataclouder/ngx-core';
|
|
25
25
|
import { PopoverModule } from 'primeng/popover';
|
|
26
|
+
import * as i4$1 from 'primeng/tag';
|
|
27
|
+
import { TagModule } from 'primeng/tag';
|
|
26
28
|
import * as i2$2 from 'primeng/speeddial';
|
|
27
29
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
28
30
|
import * as i3 from 'primeng/card';
|
|
@@ -32,16 +34,18 @@ import { map } from 'rxjs';
|
|
|
32
34
|
import BalloonEditor from '@ckeditor/ckeditor5-build-balloon-block';
|
|
33
35
|
import * as i3$1 from '@ckeditor/ckeditor5-angular';
|
|
34
36
|
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
|
|
35
|
-
import * as i5$
|
|
37
|
+
import * as i5$2 from 'primeng/splitter';
|
|
36
38
|
import { SplitterModule } from 'primeng/splitter';
|
|
37
39
|
import * as i2$5 from 'primeng/tooltip';
|
|
38
40
|
import { TooltipModule } from 'primeng/tooltip';
|
|
39
41
|
import { ResolutionType, AspectType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
|
|
40
|
-
import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
42
|
+
import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, EvalResultStringDefinition, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
41
43
|
import * as i2$4 from 'primeng/drawer';
|
|
42
44
|
import { DrawerModule } from 'primeng/drawer';
|
|
43
45
|
import TurndownService from 'turndown';
|
|
44
46
|
import { marked } from 'marked';
|
|
47
|
+
import * as i5$1 from 'primeng/inputgroup';
|
|
48
|
+
import { InputGroupModule } from 'primeng/inputgroup';
|
|
45
49
|
import * as i2$6 from 'primeng/api';
|
|
46
50
|
|
|
47
51
|
class ComponentBuilder {
|
|
@@ -420,7 +424,6 @@ function getLessonComponentClass(type) {
|
|
|
420
424
|
// import { UntypedFormControl } from '@angular/forms';
|
|
421
425
|
// import { LessonComponents } from '../models/lessons.class';
|
|
422
426
|
const LESSONS_TOKEN = new InjectionToken('Lessons Service');
|
|
423
|
-
// Think on someway of getting the toast alerts service
|
|
424
427
|
class LessonsAbstractService {
|
|
425
428
|
}
|
|
426
429
|
// my-service.provider.ts
|
|
@@ -658,6 +661,7 @@ class DcLessonCardComponent {
|
|
|
658
661
|
];
|
|
659
662
|
}
|
|
660
663
|
ngOnInit() {
|
|
664
|
+
console.log(this.lesson);
|
|
661
665
|
if (this.lesson?.media?.images[0]?.url) {
|
|
662
666
|
this.coverUrl = this.lesson.media.images[0].url;
|
|
663
667
|
}
|
|
@@ -679,11 +683,11 @@ class DcLessonCardComponent {
|
|
|
679
683
|
}
|
|
680
684
|
}
|
|
681
685
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
682
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n
|
|
686
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n }\n <p-card>\n <div class=\"lesson-card\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title }}</h1>\n <p>{{ lesson.description }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\n @if (lesson.taken?.status == 'passed') {\n <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=\"info\" value=\"\uD83D\uDCD6\" [rounded]=\"true\" />\n }\n </div>\n\n <div class=\"actions\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem}.description p{margin-bottom:1rem;flex-grow:1}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}.actions ::ng-deep .p-button{font-size:.9rem}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$2.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$1.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }] }); }
|
|
683
687
|
}
|
|
684
688
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, decorators: [{
|
|
685
689
|
type: Component,
|
|
686
|
-
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule], template: "<div class=\"card-container\">\n @if(showOptions){\n
|
|
690
|
+
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n }\n <p-card>\n <div class=\"lesson-card\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.createdDate | date : 'dd/MM/yyyy' }}</span>\n\n <div class=\"description\">\n <h1>{{ lesson.title }}</h1>\n <p>{{ lesson.description }}</p>\n <div class=\"card-footer\">\n <div class=\"status-tags\">\n <span class=\"level-tag\">Nivel {{ lesson.level }}</span>\n @if (lesson.taken?.status == 'passed') {\n <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=\"info\" value=\"\uD83D\uDCD6\" [rounded]=\"true\" />\n }\n </div>\n\n <div class=\"actions\">\n <p-button label=\"Tomar lecci\u00F3n\" (onClick)=\"eventCard(eventType.Select)\" severity=\"primary\"> </p-button>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n", styles: [".card-container{position:relative;margin-bottom:20px;margin-left:10px}.dial-button{position:absolute;top:10px;right:20px;z-index:10}.lesson-card{border-radius:.5rem;height:100%}.lesson-card .photo{position:absolute;inset:0;z-index:1}.lesson-card .photo img{width:100%;height:100%;object-fit:cover;object-position:center}.lesson-card .photo:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(to bottom,rgb(0,0,0) 50%,var(--p-primary-color) 100%);opacity:.4;z-index:2;pointer-events:none}.description{position:relative;z-index:2;color:#fff;height:100%;display:flex;flex-direction:column}.description h1{margin-top:0;margin-bottom:.5rem;font-size:1.5rem}.description p{margin-bottom:1rem;flex-grow:1}.date{position:absolute;top:1rem;left:1rem;z-index:3;background-color:#000000b3;color:#fff;padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.card-footer{display:flex;justify-content:space-between;align-items:center;margin-top:auto}.status-tags{display:flex;gap:.5rem}.status-tags .level-tag,.status-tags .status-tag{padding:.3rem .6rem;border-radius:.25rem;font-size:.8rem}.status-tags .level-tag{background-color:#fff3}.status-tags .status-tag.success{background-color:#28a745b3}.status-tags .status-tag.danger{background-color:#dc3545b3}.actions ::ng-deep .p-button{font-size:.9rem}\n"] }]
|
|
687
691
|
}], propDecorators: { lesson: [{
|
|
688
692
|
type: Input
|
|
689
693
|
}], showOptions: [{
|
|
@@ -803,15 +807,15 @@ class DCLessonListComponent extends PaginationBase {
|
|
|
803
807
|
}
|
|
804
808
|
}
|
|
805
809
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.Router }, { token: i1$2.ActivatedRoute }, { token: LESSONS_TOKEN }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
806
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { showOptions: "showOptions", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:
|
|
810
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { showOptions: "showOptions", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding: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: ["isAdmin", "items", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
|
|
807
811
|
}
|
|
808
812
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, decorators: [{
|
|
809
813
|
type: Component,
|
|
810
|
-
args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:
|
|
814
|
+
args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding: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"] }]
|
|
811
815
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.Router }, { type: i1$2.ActivatedRoute }, { type: LessonsAbstractService, decorators: [{
|
|
812
816
|
type: Inject,
|
|
813
817
|
args: [LESSONS_TOKEN]
|
|
814
|
-
}] }, { type: i4$
|
|
818
|
+
}] }, { type: i4$2.ToastAlertsAbstractService, decorators: [{
|
|
815
819
|
type: Inject,
|
|
816
820
|
args: [TOAST_ALERTS_TOKEN]
|
|
817
821
|
}] }], propDecorators: { showOptions: [{
|
|
@@ -959,6 +963,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
959
963
|
}]
|
|
960
964
|
}] });
|
|
961
965
|
|
|
966
|
+
const EnglishEvaluationSkill = `
|
|
967
|
+
You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
|
|
968
|
+
|
|
969
|
+
You will be provided with two pieces of information for each evaluation:
|
|
970
|
+
1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
|
|
971
|
+
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: ...").
|
|
972
|
+
|
|
973
|
+
Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
|
|
974
|
+
|
|
975
|
+
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:
|
|
976
|
+
|
|
977
|
+
* **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
|
|
978
|
+
* **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).
|
|
979
|
+
* **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).
|
|
980
|
+
* **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
|
|
981
|
+
|
|
982
|
+
Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
|
|
983
|
+
|
|
984
|
+
* **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
|
|
985
|
+
* **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
|
|
986
|
+
* **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
|
|
987
|
+
* **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
|
|
988
|
+
|
|
989
|
+
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.
|
|
990
|
+
`;
|
|
991
|
+
|
|
962
992
|
class DCLessonRendererComponent {
|
|
963
993
|
constructor() {
|
|
964
994
|
// --- Signal Inputs ---
|
|
@@ -971,12 +1001,14 @@ class DCLessonRendererComponent {
|
|
|
971
1001
|
this.toastrService = inject(TOAST_ALERTS_TOKEN);
|
|
972
1002
|
this.lessonService = inject(LESSONS_TOKEN);
|
|
973
1003
|
this.lessonAIService = inject(LessonAIService); // Inject the new service
|
|
1004
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
974
1005
|
// --- State Signals ---
|
|
975
1006
|
this.lesson = signal(undefined); // Internal lesson state signal
|
|
976
1007
|
this.chatVisible = signal(false); // Signal for chat visibility
|
|
977
1008
|
this.agentMasterLesson = signal(undefined); // Signal for agent card
|
|
978
1009
|
this.evaluatorAgentCard = signal(undefined); // Signal for evaluator card
|
|
979
1010
|
this.conversationSettings = signal(undefined);
|
|
1011
|
+
this.evalAgentTask = signal(undefined); // Signal for evaluator card
|
|
980
1012
|
// --- Computed Signals ---
|
|
981
1013
|
this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url); // Computed signal for imageCover
|
|
982
1014
|
// --- Properties ---
|
|
@@ -1017,6 +1049,12 @@ class DCLessonRendererComponent {
|
|
|
1017
1049
|
effect(() => {
|
|
1018
1050
|
const currentLesson = this.lesson(); // Read the lesson signal
|
|
1019
1051
|
const newTextCoded = currentLesson?.textCoded; // Get the current textCoded value
|
|
1052
|
+
this.evalAgentTask.set({
|
|
1053
|
+
systemPrompt: EnglishEvaluationSkill,
|
|
1054
|
+
model: { provider: 'google' },
|
|
1055
|
+
task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
|
|
1056
|
+
expectedResponseType: EvalResultStringDefinition,
|
|
1057
|
+
});
|
|
1020
1058
|
// Quick fix to only render on changes textCode not all the lesson
|
|
1021
1059
|
if (newTextCoded !== this.previousTextCoded) {
|
|
1022
1060
|
if (newTextCoded !== undefined && newTextCoded !== null && currentLesson) {
|
|
@@ -1190,7 +1228,7 @@ class DCLessonRendererComponent {
|
|
|
1190
1228
|
this.toastrService.error({ subtitle: 'Cannot save result, lesson data missing.', title: 'Error' });
|
|
1191
1229
|
return;
|
|
1192
1230
|
}
|
|
1193
|
-
const takenLesson = {
|
|
1231
|
+
const takenLesson = { id: currentLesson.id, goalCompleted: null, score: rates.score, status: status, lastAccess: new Date() };
|
|
1194
1232
|
console.log('Lesson evaluation result:', takenLesson);
|
|
1195
1233
|
// TODO: Re-implement saving the taken lesson status via lessonService
|
|
1196
1234
|
// try {
|
|
@@ -1224,8 +1262,6 @@ class DCLessonRendererComponent {
|
|
|
1224
1262
|
// Call the service to get the agent cards
|
|
1225
1263
|
const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(currentLesson);
|
|
1226
1264
|
if (conversationSettings) {
|
|
1227
|
-
// this.agentMasterLesson.set(agentCards);
|
|
1228
|
-
// this.evaluatorAgentCard.set(agentCards.evaluatorAgent);
|
|
1229
1265
|
this.conversationSettings.set(conversationSettings);
|
|
1230
1266
|
this.chatVisible.set(true);
|
|
1231
1267
|
console.log('Agent cards received and set.');
|
|
@@ -1240,12 +1276,24 @@ class DCLessonRendererComponent {
|
|
|
1240
1276
|
this.toastrService.error({ subtitle: 'An error occurred while preparing the AI chat.', title: 'Error' });
|
|
1241
1277
|
}
|
|
1242
1278
|
}
|
|
1279
|
+
onVisibleChange(isVisible) {
|
|
1280
|
+
if (isVisible === false) {
|
|
1281
|
+
this.chatVisible.set(false);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
handleGoalCompleted(event) {
|
|
1285
|
+
console.log('Goal completed:', event);
|
|
1286
|
+
const lesson = this.lesson();
|
|
1287
|
+
const dummy = { id: lesson.id, goalCompleted: null, score: 100, status: 'finished', lastAccess: new Date() };
|
|
1288
|
+
// TODO: finish this implementation
|
|
1289
|
+
this.lessonService.saveTakenLesson(dummy);
|
|
1290
|
+
}
|
|
1243
1291
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1244
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, test: { classPropertyName: "test", publicName: "test", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, static: true }], ngImport: i0, template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [conversationSettings]=\"conversationSettings()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "agentCard", "evaluatorAgentCard", "parseDict"], outputs: ["sendMessage", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$4.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }] }); }
|
|
1292
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, test: { classPropertyName: "test", publicName: "test", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, static: true }], ngImport: i0, template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" (visibleChange)=\"onVisibleChange($event)\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [appAgentTask]=\"evalAgentTask()\" [conversationSettings]=\"conversationSettings()\" (goalCompleted)=\"handleGoalCompleted($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "agentCard", "evaluatorAgentCard", "appAgentTask", "parseDict"], outputs: ["sendMessage", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$4.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }] }); }
|
|
1245
1293
|
}
|
|
1246
1294
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
|
|
1247
1295
|
type: Component,
|
|
1248
|
-
args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [conversationSettings]=\"conversationSettings()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"] }]
|
|
1296
|
+
args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" (visibleChange)=\"onVisibleChange($event)\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [appAgentTask]=\"evalAgentTask()\" [conversationSettings]=\"conversationSettings()\" (goalCompleted)=\"handleGoalCompleted($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"] }]
|
|
1249
1297
|
}], ctorParameters: () => [], propDecorators: { dynamicLesson: [{
|
|
1250
1298
|
type: ViewChild,
|
|
1251
1299
|
args: ['dynamicLesson', { static: true }]
|
|
@@ -1530,9 +1578,9 @@ class LessonUtilsService {
|
|
|
1530
1578
|
/**
|
|
1531
1579
|
* Improves the provided Markdown text using AI and updates the lesson.
|
|
1532
1580
|
* Assumes the lesson is already saved before calling this.
|
|
1533
|
-
* @param
|
|
1581
|
+
* @param lesson The lesson object containing the prompt and other context.
|
|
1534
1582
|
* @param markdownText The Markdown text generated from the lesson's HTML content.
|
|
1535
|
-
* @returns The
|
|
1583
|
+
* @returns The improved Markdown string, or null on failure.
|
|
1536
1584
|
*/
|
|
1537
1585
|
async improveMDWithAI(lesson, markdownText) {
|
|
1538
1586
|
if (!markdownText) {
|
|
@@ -1556,23 +1604,23 @@ class LessonUtilsService {
|
|
|
1556
1604
|
// Remove potential markdown fences
|
|
1557
1605
|
const markdownFenceStart = '```markdown\n';
|
|
1558
1606
|
const markdownFenceEnd = '\n```';
|
|
1559
|
-
if (improvedMarkdown
|
|
1607
|
+
if (improvedMarkdown?.startsWith(markdownFenceStart) && improvedMarkdown.endsWith(markdownFenceEnd)) {
|
|
1560
1608
|
improvedMarkdown = improvedMarkdown.slice(markdownFenceStart.length, -markdownFenceEnd.length);
|
|
1561
1609
|
}
|
|
1562
1610
|
else {
|
|
1563
1611
|
// Also handle cases where it might just be ``` at the start/end
|
|
1564
1612
|
const simpleFence = '```';
|
|
1565
|
-
if (improvedMarkdown
|
|
1613
|
+
if (improvedMarkdown?.startsWith(simpleFence)) {
|
|
1566
1614
|
improvedMarkdown = improvedMarkdown.slice(simpleFence.length);
|
|
1567
1615
|
}
|
|
1568
|
-
if (improvedMarkdown
|
|
1616
|
+
if (improvedMarkdown?.endsWith(simpleFence)) {
|
|
1569
1617
|
improvedMarkdown = improvedMarkdown.slice(0, -simpleFence.length);
|
|
1570
1618
|
}
|
|
1571
1619
|
}
|
|
1572
1620
|
// Trim any leading/trailing whitespace that might remain after removing fences
|
|
1573
|
-
improvedMarkdown = improvedMarkdown
|
|
1621
|
+
improvedMarkdown = improvedMarkdown?.trim() ?? null;
|
|
1574
1622
|
console.log('Improved Markdown:', improvedMarkdown);
|
|
1575
|
-
return improvedMarkdown;
|
|
1623
|
+
return improvedMarkdown; // Return only the string
|
|
1576
1624
|
}
|
|
1577
1625
|
catch (error) {
|
|
1578
1626
|
console.error('Error during AI Markdown improvement in service:', error);
|
|
@@ -1582,7 +1630,91 @@ class LessonUtilsService {
|
|
|
1582
1630
|
finally {
|
|
1583
1631
|
this.loadingBarService.successAndHide();
|
|
1584
1632
|
}
|
|
1585
|
-
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Generates a concise description for the lesson using AI based on its content.
|
|
1636
|
+
* @param lesson The lesson object containing the content.
|
|
1637
|
+
* @returns A promise resolving to the generated description string, or null on failure.
|
|
1638
|
+
*/
|
|
1639
|
+
async generateDescriptionWithAI(lesson) {
|
|
1640
|
+
if (!lesson || !lesson.textCoded) {
|
|
1641
|
+
this.#toastService.warn({ title: 'Contenido Requerido', subtitle: 'Se necesita contenido en la lección para generar una descripción.' });
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
try {
|
|
1645
|
+
this.loadingBarService.showIndeterminate(); // Corrected: No argument needed
|
|
1646
|
+
// Extract plain text from the lesson's HTML content
|
|
1647
|
+
const plainTextContent = this._extractTextFromHtml(lesson.textCoded);
|
|
1648
|
+
if (!plainTextContent || plainTextContent.trim().length === 0) {
|
|
1649
|
+
this.#toastService.warn({ title: 'Texto Vacío', subtitle: 'No se pudo extraer texto útil del contenido de la lección.' });
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
const agentCard = BasicAgentCard$1; // Use the same agent card for consistency
|
|
1653
|
+
// Craft the prompt specifically for description generation
|
|
1654
|
+
// TODO: languages shoudl be in Target language, not hardcoded
|
|
1655
|
+
const languageDescription = LangCodeDescription[lesson?.baseLang] || 'Spanish';
|
|
1656
|
+
const task = `Generate a concise and engaging description (max 3-5 sentences, ~250 characters) for a language lesson based on the following content summary.
|
|
1657
|
+
Focus on the key learning points or topic. Return ONLY the description text, without any labels or markdown formatting:
|
|
1658
|
+
and the description should be in ${languageDescription}.
|
|
1659
|
+
\n\n${plainTextContent.substring(0, 1500)}`; // Limit input text length
|
|
1660
|
+
const appTask = { task: task };
|
|
1661
|
+
const messages = [
|
|
1662
|
+
{ content: agentCard.systemPrompt, role: ChatRole.System },
|
|
1663
|
+
// Optional: Add a more specific system message for description generation if needed
|
|
1664
|
+
// { content: "You are an AI assistant specialized in creating brief, compelling descriptions for educational language lessons.", role: ChatRole.System },
|
|
1665
|
+
{ content: appTask.task, role: ChatRole.User },
|
|
1666
|
+
];
|
|
1667
|
+
const response = await this.#agentService.callChatCompletion({ messages, model: agentCard.model }); // Use the model defined in BasicAgentCard
|
|
1668
|
+
let generatedDescription = response.content?.trim() ?? null;
|
|
1669
|
+
// Basic cleanup (remove potential quotes if the AI wraps the response)
|
|
1670
|
+
if (generatedDescription && generatedDescription.startsWith('"') && generatedDescription.endsWith('"')) {
|
|
1671
|
+
generatedDescription = generatedDescription.substring(1, generatedDescription.length - 1);
|
|
1672
|
+
}
|
|
1673
|
+
if (generatedDescription) {
|
|
1674
|
+
this.#toastService.success({ title: 'Descripción Generada', subtitle: 'Se generó la descripción con IA.' });
|
|
1675
|
+
return generatedDescription;
|
|
1676
|
+
}
|
|
1677
|
+
else {
|
|
1678
|
+
throw new Error('AI did not return a valid description.');
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
catch (error) {
|
|
1682
|
+
console.error('Error during AI description generation in service:', error);
|
|
1683
|
+
this.#toastService.error({ title: 'Error de IA', subtitle: 'No se pudo generar la descripción con IA.' });
|
|
1684
|
+
return null;
|
|
1685
|
+
}
|
|
1686
|
+
finally {
|
|
1687
|
+
this.loadingBarService.successAndHide();
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Extracts plain text from an HTML string, removing encoded JSON components.
|
|
1692
|
+
* @param htmlInput The HTML string potentially containing encoded JSON (~...~).
|
|
1693
|
+
* @returns The plain text representation.
|
|
1694
|
+
*/
|
|
1695
|
+
_extractTextFromHtml(htmlInput) {
|
|
1696
|
+
if (!htmlInput)
|
|
1697
|
+
return '';
|
|
1698
|
+
// 1. Replace encoded JSON blocks with their 'settings.text' or an empty string
|
|
1699
|
+
const jsonRegex = /~(.+?)~/g;
|
|
1700
|
+
const textWithPlaceholders = htmlInput.replace(jsonRegex, (match, jsonCoded) => {
|
|
1701
|
+
try {
|
|
1702
|
+
const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1703
|
+
const data = JSON.parse(decodedJson);
|
|
1704
|
+
return data?.settings?.text || ''; // Return text or empty string if not found
|
|
1705
|
+
}
|
|
1706
|
+
catch (error) {
|
|
1707
|
+
console.error('Failed to parse JSON inside HTML during text extraction:', jsonCoded, error);
|
|
1708
|
+
return ''; // Return empty string on error
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
// 2. Strip remaining HTML tags to get plain text
|
|
1712
|
+
// Create a temporary DOM element to parse the HTML
|
|
1713
|
+
const tempDiv = document.createElement('div');
|
|
1714
|
+
tempDiv.innerHTML = textWithPlaceholders;
|
|
1715
|
+
// Use textContent to get the plain text, effectively stripping tags
|
|
1716
|
+
// Replace multiple whitespace characters (including newlines) with a single space
|
|
1717
|
+
return (tempDiv.textContent || tempDiv.innerText || '').replace(/\s+/g, ' ').trim();
|
|
1586
1718
|
}
|
|
1587
1719
|
/**
|
|
1588
1720
|
* Cleans orphaned components from the lesson's dynamicComponents.
|
|
@@ -1694,16 +1826,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1694
1826
|
class DCLessonMetadataEditorComponent {
|
|
1695
1827
|
constructor() {
|
|
1696
1828
|
// Use signal for input lesson data
|
|
1697
|
-
this.lesson = signal(undefined);
|
|
1698
|
-
this.isLoadingLesson = signal(false);
|
|
1829
|
+
this.lesson = signal(undefined); // The lesson data itself
|
|
1830
|
+
this.isLoadingLesson = signal(false); // Shared loading state
|
|
1699
1831
|
// Outputs for actions
|
|
1700
1832
|
this.saveRequest = new EventEmitter();
|
|
1701
1833
|
this.importNotionRequest = new EventEmitter();
|
|
1702
1834
|
this.improveNotionRequest = new EventEmitter();
|
|
1703
|
-
|
|
1835
|
+
// Removed generateAIRequest Output as it's handled internally now
|
|
1704
1836
|
// Output for property changes
|
|
1705
|
-
this.propertyChange = new EventEmitter();
|
|
1837
|
+
this.propertyChange = new EventEmitter(); // To update parent signal
|
|
1838
|
+
// Injected Services
|
|
1839
|
+
this.#lessonUtilsService = inject(LessonUtilsService);
|
|
1840
|
+
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1841
|
+
this.#lessonService = inject(LESSONS_TOKEN);
|
|
1842
|
+
this.#turndownService = new TurndownService(); // Instantiate TurndownService
|
|
1706
1843
|
}
|
|
1844
|
+
// Injected Services
|
|
1845
|
+
#lessonUtilsService;
|
|
1846
|
+
#toastService;
|
|
1847
|
+
#lessonService;
|
|
1848
|
+
#turndownService; // Instantiate TurndownService
|
|
1707
1849
|
// Method to emit property changes, called by ngModelChange in the template
|
|
1708
1850
|
onPropertyChange(property, value) {
|
|
1709
1851
|
this.propertyChange.emit({ property, value });
|
|
@@ -1718,13 +1860,108 @@ class DCLessonMetadataEditorComponent {
|
|
|
1718
1860
|
emitImproveNotionRequest() {
|
|
1719
1861
|
this.improveNotionRequest.emit();
|
|
1720
1862
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1863
|
+
/**
|
|
1864
|
+
* Generates lesson content using AI, saving the current state first.
|
|
1865
|
+
* Moved from DCLessonEditorComponent.
|
|
1866
|
+
*/
|
|
1867
|
+
async generateByAI() {
|
|
1868
|
+
const currentLessonValue = this.lesson(); // Get current value
|
|
1869
|
+
if (!currentLessonValue?.id) {
|
|
1870
|
+
this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
this.isLoadingLesson.set(true);
|
|
1874
|
+
try {
|
|
1875
|
+
// Ensure latest changes are saved before generating
|
|
1876
|
+
// Clean orphaned components before saving (using the injected service)
|
|
1877
|
+
const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLessonValue);
|
|
1878
|
+
const savedLesson = await this.#lessonService.postLesson(lessonToSave);
|
|
1879
|
+
if (!savedLesson) {
|
|
1880
|
+
// Handle save error - toast is shown in saveLesson (assuming postLesson shows toast on error)
|
|
1881
|
+
this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudo guardar antes de generar con IA.' });
|
|
1882
|
+
throw new Error('Failed to save before AI generation');
|
|
1883
|
+
}
|
|
1884
|
+
// Update the signal with the saved lesson data before proceeding
|
|
1885
|
+
this.lesson.set(savedLesson);
|
|
1886
|
+
const rawHtmlContent = savedLesson.textCoded || '';
|
|
1887
|
+
if (!rawHtmlContent) {
|
|
1888
|
+
console.warn('No HTML content found in lesson to process.');
|
|
1889
|
+
this.isLoadingLesson.set(false);
|
|
1890
|
+
return; // Exit if no content
|
|
1891
|
+
}
|
|
1892
|
+
// Replace encoded JSON with actual text before Markdown conversion
|
|
1893
|
+
const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
|
|
1894
|
+
// Convert the processed HTML (with text instead of JSON) to Markdown
|
|
1895
|
+
const markdownText = this.#turndownService.turndown(processedHtmlContent);
|
|
1896
|
+
// Use the updated lesson signal value for AI improvement
|
|
1897
|
+
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), markdownText);
|
|
1898
|
+
if (improvedMarkdown) {
|
|
1899
|
+
// Convert the improved Markdown back to HTML before setting it
|
|
1900
|
+
const improvedHtml = marked(improvedMarkdown);
|
|
1901
|
+
// Update the signal directly
|
|
1902
|
+
this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
|
|
1903
|
+
// Save the AI-generated content
|
|
1904
|
+
await this.#lessonService.postLesson(this.lesson());
|
|
1905
|
+
this.#toastService.success({ title: 'Contenido generado', subtitle: 'Se generó y guardó el contenido con IA.' });
|
|
1906
|
+
}
|
|
1907
|
+
else {
|
|
1908
|
+
// Toast error is handled by the service
|
|
1909
|
+
throw new Error('AI generation failed or lesson fetch failed after generation.');
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
catch (error) {
|
|
1913
|
+
console.error('Error during AI generation process in metadata editor:', error);
|
|
1914
|
+
// Service handles specific AI error toasts, maybe add a general one here if needed
|
|
1915
|
+
// this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
|
|
1916
|
+
}
|
|
1917
|
+
finally {
|
|
1918
|
+
this.isLoadingLesson.set(false); // Stop loading
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Replaces encoded JSON blocks (~...~) in HTML with their 'settings.text' value.
|
|
1923
|
+
* Moved from DCLessonEditorComponent.
|
|
1924
|
+
* @param htmlInput The HTML string containing encoded JSON.
|
|
1925
|
+
* @returns The processed HTML string with text replacements.
|
|
1926
|
+
*/
|
|
1927
|
+
_extractTextFromEncodedJson(htmlInput) {
|
|
1928
|
+
const jsonRegex = /~(.+?)~/g; // Global flag to replace all occurrences
|
|
1929
|
+
return htmlInput.replace(jsonRegex, (match, jsonCoded) => {
|
|
1930
|
+
try {
|
|
1931
|
+
// Attempt to decode HTML entities that might be present in the JSON string
|
|
1932
|
+
const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1933
|
+
const data = JSON.parse(decodedJson);
|
|
1934
|
+
// Return the text if available, otherwise keep the original match (or an error placeholder)
|
|
1935
|
+
return data?.settings?.text || match;
|
|
1936
|
+
}
|
|
1937
|
+
catch (error) {
|
|
1938
|
+
console.error('Failed to parse JSON inside HTML:', jsonCoded, error);
|
|
1939
|
+
return '<!-- invalid component data -->'; // Placeholder for parsing errors
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Calls the LessonUtilsService to generate a description using AI
|
|
1945
|
+
* and updates the lesson signal if successful.
|
|
1946
|
+
*/
|
|
1947
|
+
async triggerGenerateDescriptionAI() {
|
|
1948
|
+
const currentLesson = this.lesson();
|
|
1949
|
+
if (!currentLesson) {
|
|
1950
|
+
this.#toastService.warn({ title: 'Lección no cargada', subtitle: 'Espera a que la lección se cargue.' });
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
const generatedDescription = await this.#lessonUtilsService.generateDescriptionWithAI(currentLesson);
|
|
1954
|
+
if (generatedDescription) {
|
|
1955
|
+
// Use the existing propertyChange emitter to update the parent signal
|
|
1956
|
+
this.onPropertyChange('description', generatedDescription);
|
|
1957
|
+
// this.emitSaveRequest(); // optional do something to automatically save
|
|
1958
|
+
}
|
|
1723
1959
|
}
|
|
1724
1960
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1725
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest",
|
|
1961
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest", propertyChange: "propertyChange" }, ngImport: i0, template: "@if (lesson(); as currentLesson) {\n<div>\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div>\n\n <!-- Use one-way binding and ngModelChange for signals -->\n <div>\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.title\"\n (ngModelChange)=\"onPropertyChange('title', $event)\"\n type=\"text\"\n placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <p-inputgroup>\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.description\"\n (ngModelChange)=\"onPropertyChange('description', $event)\"\n type=\"text\"\n placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson()\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [ngModel]=\"currentLesson.prompt\"\n (ngModelChange)=\"onPropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson()\" (click)=\"generateByAI()\" />\n </div>\n\n <div style=\"margin-top: 10px\">\n <label class=\"checkbox-container\" style=\"margin-right: 15px\">\n <input\n type=\"checkbox\"\n [ngModel]=\"currentLesson.isPublished\"\n (ngModelChange)=\"onPropertyChange('isPublished', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n\n <input\n pInputText\n [ngModel]=\"currentLesson.level\"\n (ngModelChange)=\"onPropertyChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n </div>\n\n <!-- Access signal values -->\n <div style=\"margin-top: 10px; font-size: 0.9em; color: var(--text-color-secondary)\">\n {{ currentLesson.baseLang | flagEmoji }} -> {{ currentLesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.baseLang | langDesc : 'es' }} que aprenden\n {{ currentLesson.targetLang | langDesc : 'es' }}\n </div>\n </div>\n</div>\n} @else {\n<!-- Optional: Show a loading state or placeholder if lesson is undefined -->\n<p>Cargando datos de la lecci\u00F3n...</p>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "pipe", type: // Added TooltipModule
|
|
1726
1962
|
FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
|
|
1727
|
-
LangDescTranslationPipe, name: "langDesc" }
|
|
1963
|
+
LangDescTranslationPipe, name: "langDesc" }, { kind: "ngmodule", type: // Added Pipe
|
|
1964
|
+
InputGroupModule }, { kind: "component", type: i5$1.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["style", "styleClass"] }] }); }
|
|
1728
1965
|
}
|
|
1729
1966
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, decorators: [{
|
|
1730
1967
|
type: Component,
|
|
@@ -1736,7 +1973,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1736
1973
|
TooltipModule, // Added TooltipModule
|
|
1737
1974
|
FlagLanguagePipe, // Added Pipe
|
|
1738
1975
|
LangDescTranslationPipe, // Added Pipe
|
|
1739
|
-
|
|
1976
|
+
InputGroupModule,
|
|
1977
|
+
], template: "@if (lesson(); as currentLesson) {\n<div>\n <div>\n <div style=\"display: flex; gap: 10px; padding: 10px\">\n <p-button label=\"Guardar\" severity=\"primary\" (click)=\"emitSaveRequest()\" />\n <p-button label=\"Importar de Notion\" severity=\"help\" (click)=\"emitImportNotionRequest()\" />\n <p-button label=\"Mejorar Notion con AI\" severity=\"help\" (click)=\"emitImproveNotionRequest()\" />\n </div>\n\n <!-- Use one-way binding and ngModelChange for signals -->\n <div>\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.title\"\n (ngModelChange)=\"onPropertyChange('title', $event)\"\n type=\"text\"\n placeholder=\"Agrega un t\u00EDtulo\" />\n </div>\n <div style=\"margin-top: 4px\">\n <p-inputgroup>\n <input\n pInputText\n style=\"width: 100%\"\n [ngModel]=\"currentLesson.description\"\n (ngModelChange)=\"onPropertyChange('description', $event)\"\n type=\"text\"\n placeholder=\"Agrega una descripci\u00F3n\" />\n <p-button\n icon=\"pi pi-sparkles\"\n styleClass=\"p-button-secondary p-button-outlined\"\n pTooltip=\"Generar descripci\u00F3n con IA\"\n tooltipPosition=\"top\"\n [disabled]=\"isLoadingLesson()\"\n (click)=\"triggerGenerateDescriptionAI()\" />\n </p-inputgroup>\n </div>\n\n <div style=\"display: flex; align-items: center; margin-top: 10px\">\n <input\n pInputText\n style=\"flex: auto; margin-right: 5px\"\n [ngModel]=\"currentLesson.prompt\"\n (ngModelChange)=\"onPropertyChange('prompt', $event)\"\n type=\"text\"\n placeholder=\"Prompt para IA (opcional)\" />\n <p-button severity=\"primary\" label=\"Generar con IA\" icon=\"pi pi-sparkles\" [disabled]=\"isLoadingLesson()\" (click)=\"generateByAI()\" />\n </div>\n\n <div style=\"margin-top: 10px\">\n <label class=\"checkbox-container\" style=\"margin-right: 15px\">\n <input\n type=\"checkbox\"\n [ngModel]=\"currentLesson.isPublished\"\n (ngModelChange)=\"onPropertyChange('isPublished', $event)\"\n title=\"Cuando termines la edici\u00F3n marca esta casilla\" />\n <span class=\"checkmark\"></span>\n Publicada\n </label>\n\n <input\n pInputText\n [ngModel]=\"currentLesson.level\"\n (ngModelChange)=\"onPropertyChange('level', $event)\"\n type=\"number\"\n placeholder=\"Nivel\"\n style=\"width: 80px\" />\n </div>\n\n <!-- Access signal values -->\n <div style=\"margin-top: 10px; font-size: 0.9em; color: var(--text-color-secondary)\">\n {{ currentLesson.baseLang | flagEmoji }} -> {{ currentLesson.targetLang | flagEmoji }} Lecci\u00F3n para hablantes de\n {{ currentLesson.baseLang | langDesc : 'es' }} que aprenden\n {{ currentLesson.targetLang | langDesc : 'es' }}\n </div>\n </div>\n</div>\n} @else {\n<!-- Optional: Show a loading state or placeholder if lesson is undefined -->\n<p>Cargando datos de la lecci\u00F3n...</p>\n}\n" }]
|
|
1740
1978
|
}], propDecorators: { lesson: [{
|
|
1741
1979
|
type: Input,
|
|
1742
1980
|
args: [{ required: true }]
|
|
@@ -1749,8 +1987,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1749
1987
|
type: Output
|
|
1750
1988
|
}], improveNotionRequest: [{
|
|
1751
1989
|
type: Output
|
|
1752
|
-
}], generateAIRequest: [{
|
|
1753
|
-
type: Output
|
|
1754
1990
|
}], propertyChange: [{
|
|
1755
1991
|
type: Output
|
|
1756
1992
|
}] } });
|
|
@@ -1949,75 +2185,8 @@ class DCLessonEditorComponent {
|
|
|
1949
2185
|
}
|
|
1950
2186
|
}
|
|
1951
2187
|
// isLoadingLesson signal is used directly
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
if (!currentId) {
|
|
1955
|
-
this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
|
|
1956
|
-
return;
|
|
1957
|
-
}
|
|
1958
|
-
this.isLoadingLesson.set(true);
|
|
1959
|
-
try {
|
|
1960
|
-
// Ensure latest changes are saved before generating
|
|
1961
|
-
const savedLesson = await this.saveLesson();
|
|
1962
|
-
if (!savedLesson) {
|
|
1963
|
-
// Handle save error - toast is shown in saveLesson
|
|
1964
|
-
throw new Error('Failed to save before AI generation');
|
|
1965
|
-
}
|
|
1966
|
-
// // Removed debugger
|
|
1967
|
-
const rawHtmlContent = this.lesson()?.textCoded || '';
|
|
1968
|
-
if (!rawHtmlContent) {
|
|
1969
|
-
console.warn('No HTML content found in lesson to process.');
|
|
1970
|
-
this.isLoadingLesson.set(false);
|
|
1971
|
-
return; // Exit if no content
|
|
1972
|
-
}
|
|
1973
|
-
// Replace encoded JSON with actual text before Markdown conversion
|
|
1974
|
-
const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
|
|
1975
|
-
const turndownService = new TurndownService();
|
|
1976
|
-
// Convert the processed HTML (with text instead of JSON) to Markdown
|
|
1977
|
-
const markdownText = turndownService.turndown(processedHtmlContent);
|
|
1978
|
-
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), markdownText);
|
|
1979
|
-
if (improvedMarkdown) {
|
|
1980
|
-
// Convert the improved Markdown back to HTML before setting it
|
|
1981
|
-
const improvedHtml = marked(improvedMarkdown);
|
|
1982
|
-
this.lesson.set({ ...this.lesson(), textCoded: improvedHtml }); // Update the signal with the converted HTML
|
|
1983
|
-
// Toast success is handled by the service
|
|
1984
|
-
}
|
|
1985
|
-
else {
|
|
1986
|
-
// Toast error is handled by the service
|
|
1987
|
-
throw new Error('AI generation failed or lesson fetch failed after generation.');
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
catch (error) {
|
|
1991
|
-
// Type error
|
|
1992
|
-
console.error('Error during AI generation process in component:', error);
|
|
1993
|
-
// Service handles specific AI error toasts, maybe add a general one here if needed
|
|
1994
|
-
// this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
|
|
1995
|
-
}
|
|
1996
|
-
finally {
|
|
1997
|
-
this.isLoadingLesson.set(false); // Stop loading
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Replaces encoded JSON blocks (~...~) in HTML with their 'settings.text' value.
|
|
2002
|
-
* @param htmlInput The HTML string containing encoded JSON.
|
|
2003
|
-
* @returns The processed HTML string with text replacements.
|
|
2004
|
-
*/
|
|
2005
|
-
_extractTextFromEncodedJson(htmlInput) {
|
|
2006
|
-
const jsonRegex = /~(.+?)~/g; // Global flag to replace all occurrences
|
|
2007
|
-
return htmlInput.replace(jsonRegex, (match, jsonCoded) => {
|
|
2008
|
-
try {
|
|
2009
|
-
// Attempt to decode HTML entities that might be present in the JSON string
|
|
2010
|
-
const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
2011
|
-
const data = JSON.parse(decodedJson);
|
|
2012
|
-
// Return the text if available, otherwise keep the original match (or an error placeholder)
|
|
2013
|
-
return data?.settings?.text || match;
|
|
2014
|
-
}
|
|
2015
|
-
catch (error) {
|
|
2016
|
-
console.error('Failed to parse JSON inside HTML:', jsonCoded, error);
|
|
2017
|
-
return '<!-- invalid component data -->'; // Placeholder for parsing errors
|
|
2018
|
-
}
|
|
2019
|
-
});
|
|
2020
|
-
}
|
|
2188
|
+
// Removed generateByAI and _extractTextFromEncodedJson methods.
|
|
2189
|
+
// This logic is now handled within DCLessonMetadataEditorComponent.
|
|
2021
2190
|
/**
|
|
2022
2191
|
* Handles the image upload event, updates the lesson signal via the service, and saves.
|
|
2023
2192
|
* @param event The image upload event data.
|
|
@@ -2060,8 +2229,8 @@ class DCLessonEditorComponent {
|
|
|
2060
2229
|
alert('showComponentDetails' + JSON.stringify(data));
|
|
2061
2230
|
}
|
|
2062
2231
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2063
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], viewQueries: [{ propertyName: "target", first: true, predicate: ["target"], descendants: true, read: ViewContainerRef }, { propertyName: "dhtml", first: true, predicate: ["dhtml"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (
|
|
2064
|
-
DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "
|
|
2232
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], viewQueries: [{ propertyName: "target", first: true, predicate: ["target"], descendants: true, read: ViewContainerRef }, { propertyName: "dhtml", first: true, predicate: ["dhtml"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (propertyChange)=\"updateLessonProperty($event.property, $event.value)\">\n</dc-lesson-metadata-editor>\n\n<hr />\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li>ID: {{ comp.id }} - Tipo: {{ comp.component }}<button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button></li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"lesson()?.textCoded\"\n (ngModelChange)=\"updateLessonProperty('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"lesson()\" [test]=\"true\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <!-- Removed p-speeddial -->\n <p-button icon=\"pi pi-save\" (click)=\"saveLesson()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$6.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i3$1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "component", type: DCLessonRendererComponent, selector: "dc-lesson-renderer", inputs: ["lessonInput", "lessonIdInput", "test"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i5$2.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "style", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: DCLessonComponentAdderComponent, selector: "dc-lesson-component-adder", outputs: ["componentAdded"] }, { kind: "component", type: // Add the component adder here
|
|
2233
|
+
DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "propertyChange"] }] }); }
|
|
2065
2234
|
}
|
|
2066
2235
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
|
|
2067
2236
|
type: Component,
|
|
@@ -2076,7 +2245,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2076
2245
|
TooltipModule,
|
|
2077
2246
|
DCLessonComponentAdderComponent, // Add the component adder here
|
|
2078
2247
|
DCLessonMetadataEditorComponent, // Add the metadata editor here
|
|
2079
|
-
], providers: [LessonNotionService], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (
|
|
2248
|
+
], providers: [LessonNotionService], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (propertyChange)=\"updateLessonProperty($event.property, $event.value)\">\n</dc-lesson-metadata-editor>\n\n<hr />\n\n<!-- Component Adder -->\n<dc-lesson-component-adder (componentAdded)=\"onComponentAdded($event)\"></dc-lesson-component-adder>\n\n<!-- Display Added Components -->\n<div class=\"added-components-list\" style=\"margin-top: 15px; margin-bottom: 15px\">\n <h4>Componentes Agregados:</h4>\n @if (dynamicComponentsArray().length > 0) {\n <ul>\n @for (comp of dynamicComponentsArray(); track comp.id) {\n <li>ID: {{ comp.id }} - Tipo: {{ comp.component }}<button pButton icon=\"pi pi-info\" (click)=\"showComponentDetails(comp)\"></button></li>\n }\n </ul>\n } @else {\n <p>A\u00FAn no se han agregado componentes.</p>\n }\n</div>\n\n<hr />\n\n<!-- Text Editor and Renderer -->\n<p-splitter [style]=\"{ height: '80vh' }\">\n <ng-template pTemplate>\n <ckeditor\n (keydown.control.s)=\"saveLesson($event)\"\n class=\"text-editor\"\n [editor]=\"editor\"\n [ngModel]=\"lesson()?.textCoded\"\n (ngModelChange)=\"updateLessonProperty('textCoded', $event)\">\n </ckeditor>\n </ng-template>\n\n <ng-template pTemplate>\n <dc-lesson-renderer class=\"text-editor\" [lessonInput]=\"lesson()\" [test]=\"true\"></dc-lesson-renderer>\n </ng-template>\n</p-splitter>\n\n<div class=\"float-button\">\n <!-- Removed p-speeddial -->\n <p-button icon=\"pi pi-save\" (click)=\"saveLesson()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n\n<hr />\n", styles: [".btn{padding:.5rem 1rem;border-radius:4px;border:1px solid transparent;cursor:pointer}.btn-primary{background-color:#007bff;color:#fff}.btn-outline-primary{border-color:#007bff;color:#007bff}.btn-secondary{background-color:#6c757d;color:#fff}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-rounded{border-radius:50%}.form-control{padding:.375rem .75rem;border:1px solid #ced4da;border-radius:.25rem}.splitter{display:flex;gap:1rem}.splitter-panel{flex:1}.checkbox-container{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer}.mr-2{margin-right:.5rem}.header-cover{width:100%;height:250px;background-position:center;background-repeat:no-repeat;background-size:cover;position:relative}.float-button{position:fixed;bottom:3.5rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}.text-editor{width:-webkit-fill-available;overflow-y:auto}:host ::ng-deep .p-inputtext{background:#fff3}\n"] }]
|
|
2080
2249
|
}], ctorParameters: () => [], propDecorators: { target: [{
|
|
2081
2250
|
type: ViewChild,
|
|
2082
2251
|
args: ['target', { read: ViewContainerRef }]
|
|
@@ -2085,6 +2254,103 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2085
2254
|
args: ['dhtml', { static: true }]
|
|
2086
2255
|
}] } });
|
|
2087
2256
|
|
|
2257
|
+
// Define placeholder endpoints - these should be configured appropriately
|
|
2258
|
+
const LESSONS_BASE_PATH = 'api/lesson'; // Example base path
|
|
2259
|
+
class DefaultLessonsService {
|
|
2260
|
+
constructor() {
|
|
2261
|
+
this.httpCoreService = inject(HttpCoreService);
|
|
2262
|
+
// --- Endpoint Definitions (Hardcoded as requested) ---
|
|
2263
|
+
this.endpoints = {
|
|
2264
|
+
queryLessons: `${LESSONS_BASE_PATH}/query`,
|
|
2265
|
+
getLesson: (id) => `${LESSONS_BASE_PATH}/${id}`,
|
|
2266
|
+
saveLesson: `api/user/saveLesson`,
|
|
2267
|
+
updateLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming PUT to /lessons/:id
|
|
2268
|
+
deleteLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming DELETE to /lessons/:id
|
|
2269
|
+
generateLesson: `${LESSONS_BASE_PATH}/generate`, // Placeholder
|
|
2270
|
+
generateByAI: `${LESSONS_BASE_PATH}/generate-ai`, // Placeholder
|
|
2271
|
+
improveMDWithAI: `${LESSONS_BASE_PATH}/improve-markdown-ai`, // Placeholder
|
|
2272
|
+
QueryLessons: 'api/lesson/query',
|
|
2273
|
+
Lesson: 'api/lesson',
|
|
2274
|
+
SaveLesson: 'api/lesson-polilan',
|
|
2275
|
+
GetPublicLessons: 'api/lesson/publicLessons',
|
|
2276
|
+
GetUnpublishedLessons: 'api/lesson/unpublished',
|
|
2277
|
+
TakenLesson: 'api/lesson/taken',
|
|
2278
|
+
DeleteLesson: 'api/lesson',
|
|
2279
|
+
Base: 'api/lesson',
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
saveTakenLesson(lesson) {
|
|
2283
|
+
// Not sure how to implement this yet.
|
|
2284
|
+
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2285
|
+
}
|
|
2286
|
+
// --- Method Implementations ---
|
|
2287
|
+
async getLessons(paginator) {
|
|
2288
|
+
// Assuming paginator is the body for a POST request based on the example
|
|
2289
|
+
return this.httpCoreService.post(this.endpoints.queryLessons, paginator || {});
|
|
2290
|
+
}
|
|
2291
|
+
async getLesson(id) {
|
|
2292
|
+
return this.httpCoreService.get(this.endpoints.getLesson(id));
|
|
2293
|
+
}
|
|
2294
|
+
async postLesson(lesson) {
|
|
2295
|
+
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2296
|
+
}
|
|
2297
|
+
async updateLesson(lesson) {
|
|
2298
|
+
if (!lesson._id) {
|
|
2299
|
+
throw new Error('Lesson ID is required for update.');
|
|
2300
|
+
}
|
|
2301
|
+
// Assuming _id is the identifier
|
|
2302
|
+
return this.httpCoreService.put(this.endpoints.updateLesson(lesson._id), lesson);
|
|
2303
|
+
}
|
|
2304
|
+
async deleteLesson(id) {
|
|
2305
|
+
return this.httpCoreService.delete(this.endpoints.deleteLesson(id));
|
|
2306
|
+
}
|
|
2307
|
+
async generateLesson(lesson) {
|
|
2308
|
+
// This endpoint might need specific data or structure
|
|
2309
|
+
return this.httpCoreService.post(this.endpoints.generateLesson, lesson);
|
|
2310
|
+
}
|
|
2311
|
+
async postGenerateByAI(id) {
|
|
2312
|
+
return this.httpCoreService.post(this.endpoints.generateByAI, { id });
|
|
2313
|
+
}
|
|
2314
|
+
async postImproveMDWithAI(lessonId, markdownText) {
|
|
2315
|
+
return this.httpCoreService.post(this.endpoints.improveMDWithAI, { id: lessonId, markdown: markdownText });
|
|
2316
|
+
}
|
|
2317
|
+
extractTextFromHtml(html) {
|
|
2318
|
+
// Copied from src/app/core/data-services/lessons.service.ts
|
|
2319
|
+
const r1 = new RegExp('~(.+?)~', 'g');
|
|
2320
|
+
const lessonHtml = html.replace(r1, (_matching, jsonCoded) => {
|
|
2321
|
+
try {
|
|
2322
|
+
const data = JSON.parse(jsonCoded);
|
|
2323
|
+
return `<span>${data?.settings?.text || ''}</span>`; // Added default empty string
|
|
2324
|
+
}
|
|
2325
|
+
catch (e) {
|
|
2326
|
+
console.error('Error parsing JSON in extractTextFromHtml:', jsonCoded, e);
|
|
2327
|
+
return ''; // Return empty string on error
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
// Remove HTML tags
|
|
2331
|
+
let text = lessonHtml.replace(/<[^>]*>/g, ' ');
|
|
2332
|
+
// Remove style and script content
|
|
2333
|
+
text = text.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, ' ');
|
|
2334
|
+
text = text.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, ' ');
|
|
2335
|
+
// Decode HTML entities
|
|
2336
|
+
text = text.replace(/ /g, ' ');
|
|
2337
|
+
text = text.replace(/&/g, '&');
|
|
2338
|
+
text = text.replace(/</g, '<');
|
|
2339
|
+
text = text.replace(/>/g, '>');
|
|
2340
|
+
// Remove extra whitespace
|
|
2341
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
2342
|
+
return text;
|
|
2343
|
+
}
|
|
2344
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2345
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, providedIn: 'root' }); }
|
|
2346
|
+
}
|
|
2347
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, decorators: [{
|
|
2348
|
+
type: Injectable,
|
|
2349
|
+
args: [{
|
|
2350
|
+
providedIn: 'root',
|
|
2351
|
+
}]
|
|
2352
|
+
}] });
|
|
2353
|
+
|
|
2088
2354
|
// This is the base class for all the components that are going to be used in the lessons.
|
|
2089
2355
|
class LessonDynamicComponent {
|
|
2090
2356
|
constructor() {
|
|
@@ -2112,5 +2378,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2112
2378
|
* Generated bundle index. Do not edit.
|
|
2113
2379
|
*/
|
|
2114
2380
|
|
|
2115
|
-
export { BasicAgentCard$1 as BasicAgentCard, ComponentBuilder, ComponentWithForm, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LangDescTranslationPipe, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonDynamicComponent, LessonsAbstractService, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLessonComponentClass, provideLessonsService, provideNotionService };
|
|
2381
|
+
export { BasicAgentCard$1 as BasicAgentCard, ComponentBuilder, ComponentWithForm, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, DefaultLessonsService, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LangDescTranslationPipe, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonDynamicComponent, LessonsAbstractService, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLessonComponentClass, provideLessonsService, provideNotionService };
|
|
2116
2382
|
//# sourceMappingURL=dataclouder-ngx-lessons.mjs.map
|