@dataclouder/ngx-lessons 0.0.34 → 0.0.35
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 +323 -224
- package/fesm2022/dataclouder-ngx-lessons.mjs.map +1 -1
- package/lib/components/dc-lessons/dc-lesson-card/dc-lesson-card.component.d.ts +2 -1
- package/lib/components/dc-lessons/dc-lesson-editor/dc-lesson-editor.component.d.ts +9 -0
- package/lib/components/dc-lessons/dc-lesson-metadata-editor/dc-lesson-metadata-editor.component.d.ts +4 -7
- package/lib/components/lesson-mini-components/components/lessons.clases.d.ts +18 -0
- package/lib/models/prompts.d.ts +6 -0
- package/lib/models/simple-agents.d.ts +1 -1
- package/lib/services/default-lessons.service.d.ts +3 -1
- package/lib/services/lesson-utils.service.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, Input, ChangeDetectionStrategy, signal, InjectionToken, Pipe, EventEmitter, Output, ViewChildren, Inject, inject, Injectable, input, Renderer2, ViewContainerRef, computed, effect, ViewChild } from '@angular/core';
|
|
2
|
+
import { Component, Input, ChangeDetectionStrategy, signal, InjectionToken, Pipe, EventEmitter, Output, ViewChildren, Inject, inject, Injectable, input, Renderer2, ViewContainerRef, computed, effect, ViewChild, ChangeDetectorRef } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormControl, UntypedFormControl, FormsModule, ReactiveFormsModule, Validators, FormGroup } from '@angular/forms';
|
|
5
5
|
import * as i4 from 'primeng/inputtext';
|
|
@@ -21,7 +21,7 @@ import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule } from '@angula
|
|
|
21
21
|
import * as i1$2 from '@angular/router';
|
|
22
22
|
import { RouterModule, ActivatedRoute, Router } from '@angular/router';
|
|
23
23
|
import * as i4$2 from '@dataclouder/ngx-core';
|
|
24
|
-
import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService, HttpCoreService } from '@dataclouder/ngx-core';
|
|
24
|
+
import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService, HttpCoreService, PromptService } from '@dataclouder/ngx-core';
|
|
25
25
|
import { PopoverModule } from 'primeng/popover';
|
|
26
26
|
import * as i4$1 from 'primeng/tag';
|
|
27
27
|
import { TagModule } from 'primeng/tag';
|
|
@@ -42,10 +42,13 @@ import { ResolutionType, AspectType, CropperComponentModal } from '@dataclouder/
|
|
|
42
42
|
import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, EvalResultStringDefinition, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
|
|
43
43
|
import * as i2$4 from 'primeng/drawer';
|
|
44
44
|
import { DrawerModule } from 'primeng/drawer';
|
|
45
|
+
import * as i7 from 'primeng/dialog';
|
|
46
|
+
import { DialogModule } from 'primeng/dialog';
|
|
45
47
|
import TurndownService from 'turndown';
|
|
46
48
|
import { marked } from 'marked';
|
|
47
49
|
import * as i5$1 from 'primeng/inputgroup';
|
|
48
50
|
import { InputGroupModule } from 'primeng/inputgroup';
|
|
51
|
+
import { NgxVertexService, ChatRoleVertex } from '@dataclouder/ngx-vertex';
|
|
49
52
|
import * as i2$6 from 'primeng/api';
|
|
50
53
|
|
|
51
54
|
class ComponentBuilder {
|
|
@@ -573,23 +576,26 @@ You incorporate visual dividers to separate major content sections
|
|
|
573
576
|
|
|
574
577
|
When responding to requests, you'll first understand the subject matter, then organize it into a logical structure with clear headings, appropriate formatting elements, and helpful visual enhancements. Your goal is always to create content that's not only informative but also visually engaging and accessible to all readers.
|
|
575
578
|
`;
|
|
576
|
-
const
|
|
579
|
+
const UserRequirements = `
|
|
580
|
+
User also can provide additional instructions or requirements for the lesson or current lesson to improve it.
|
|
581
|
+
`;
|
|
582
|
+
const LanguageLessonSkill = (langBase, langTarget) => `
|
|
577
583
|
|
|
578
|
-
You are also
|
|
579
|
-
explain the best you can will all your experience teaching.
|
|
584
|
+
You are also a ${langTarget} lesson professor, you write lessons for ${langBase} learners.
|
|
585
|
+
explain the best you can will all your experience teaching.
|
|
580
586
|
|
|
581
587
|
You can infer the level of the lesson from the content or user request, so can increase difficulty
|
|
582
588
|
|
|
583
|
-
For Basic Level: generate
|
|
589
|
+
For Basic Level: generate a ${langTarget} lesson for ${langBase} learners at a basic level (A1-A2).
|
|
584
590
|
|
|
585
591
|
Include:
|
|
586
592
|
|
|
587
593
|
- Simple dialogues or short texts.
|
|
588
|
-
- Key vocabulary with simple definitions or
|
|
594
|
+
- Key vocabulary with simple definitions or ${langBase} translations.
|
|
589
595
|
- Basic grammar points explained simply (e.g., "to be" verb, simple present).
|
|
590
596
|
- Practice exercises (e.g., fill-in-the-blanks, simple questions).
|
|
591
597
|
|
|
592
|
-
For Intermediate Level: generate
|
|
598
|
+
For Intermediate Level: generate a ${langTarget} lesson for ${langBase} learners at a medium level (B1-B2).
|
|
593
599
|
|
|
594
600
|
Include:
|
|
595
601
|
|
|
@@ -599,7 +605,7 @@ Include:
|
|
|
599
605
|
- Exercises that require more detailed responses or understanding.
|
|
600
606
|
- The content should be engaging and cover topics relevant to daily life, work, or hobbies.
|
|
601
607
|
|
|
602
|
-
For Advanced Level: generate
|
|
608
|
+
For Advanced Level: generate a ${langTarget} lesson for ${langBase} learners at an advanced level (C1-C2).
|
|
603
609
|
|
|
604
610
|
Include:
|
|
605
611
|
|
|
@@ -608,19 +614,17 @@ Include:
|
|
|
608
614
|
- Exploration of complex grammar or stylistic points.
|
|
609
615
|
- Exercises that encourage critical thinking, debate, or detailed writing.
|
|
610
616
|
- The content should be stimulating and cover a wide range of topics, including academic or professional contexts.
|
|
611
|
-
|
|
612
617
|
`;
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
model: { id: 'gemini-2.5-flash-preview-04-17', provider: 'google' },
|
|
618
|
+
const getLanguageSimpleAgent = (langBase, langTarget) => {
|
|
619
|
+
return {
|
|
620
|
+
systemPrompt: `
|
|
621
|
+
${MarkdownWriterSkill}
|
|
622
|
+
${LanguageLessonSkill(langBase, langTarget)}
|
|
623
|
+
${UserRequirements}
|
|
624
|
+
|
|
625
|
+
`,
|
|
626
|
+
model: { id: 'gemini-2.5-flash-preview-04-17', provider: 'google' },
|
|
627
|
+
};
|
|
624
628
|
};
|
|
625
629
|
|
|
626
630
|
var EventCard;
|
|
@@ -633,6 +637,7 @@ var EventCard;
|
|
|
633
637
|
class DcLessonCardComponent {
|
|
634
638
|
constructor() {
|
|
635
639
|
this.showOptions = true;
|
|
640
|
+
this.cardHeight = '200px';
|
|
636
641
|
this.onAction = new EventEmitter();
|
|
637
642
|
this.coverUrl = 'assets/background/default-background.webp';
|
|
638
643
|
this.eventType = EventCard;
|
|
@@ -662,9 +667,7 @@ class DcLessonCardComponent {
|
|
|
662
667
|
}
|
|
663
668
|
ngOnInit() {
|
|
664
669
|
console.log(this.lesson);
|
|
665
|
-
|
|
666
|
-
this.coverUrl = this.lesson.media.images[0].url;
|
|
667
|
-
}
|
|
670
|
+
this.coverUrl = this.lesson?.banner?.url || this.lesson?.media?.images?.[0]?.url || 'assets/background/default-background.webp';
|
|
668
671
|
}
|
|
669
672
|
eventCard(eventType) {
|
|
670
673
|
switch (eventType) {
|
|
@@ -683,15 +686,17 @@ class DcLessonCardComponent {
|
|
|
683
686
|
}
|
|
684
687
|
}
|
|
685
688
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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=\"
|
|
689
|
+
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", 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 }\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.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=\"success\" value=\"Tomada \uD83D\uDCD6\" [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}.description p{margin-bottom:1rem;flex-grow:1;font-weight:600}.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: "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"] }] }); }
|
|
687
690
|
}
|
|
688
691
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, decorators: [{
|
|
689
692
|
type: Component,
|
|
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=\"
|
|
693
|
+
args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule, TagModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <p-speeddial\n class=\"dial-button\"\n [model]=\"items\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n }\n <p-card>\n <div class=\"lesson-card\" [style.height]=\"cardHeight\">\n <div class=\"photo\">\n <img [src]=\"coverUrl\" alt=\"\" />\n </div>\n\n <span class=\"date\">{{ lesson.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=\"success\" value=\"Tomada \uD83D\uDCD6\" [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}.description p{margin-bottom:1rem;flex-grow:1;font-weight:600}.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"] }]
|
|
691
694
|
}], propDecorators: { lesson: [{
|
|
692
695
|
type: Input
|
|
693
696
|
}], showOptions: [{
|
|
694
697
|
type: Input
|
|
698
|
+
}], cardHeight: [{
|
|
699
|
+
type: Input
|
|
695
700
|
}], onAction: [{
|
|
696
701
|
type: Output
|
|
697
702
|
}] } });
|
|
@@ -1284,9 +1289,9 @@ class DCLessonRendererComponent {
|
|
|
1284
1289
|
handleGoalCompleted(event) {
|
|
1285
1290
|
console.log('Goal completed:', event);
|
|
1286
1291
|
const lesson = this.lesson();
|
|
1287
|
-
const dummy = { id: lesson.id, goalCompleted:
|
|
1288
|
-
// TODO: finish this implementation
|
|
1292
|
+
const dummy = { id: lesson.id, goalCompleted: true, score: 100, status: 'finished', lastAccess: new Date() };
|
|
1289
1293
|
this.lessonService.saveTakenLesson(dummy);
|
|
1294
|
+
this.toastrService.success({ subtitle: '¡Has completado la lección! , pero puedes seguir conversando', title: '¡Muy bien, guardaremos tu progreso!' });
|
|
1290
1295
|
}
|
|
1291
1296
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1292
1297
|
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"] }] }); }
|
|
@@ -1464,6 +1469,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1464
1469
|
type: Injectable
|
|
1465
1470
|
}] });
|
|
1466
1471
|
|
|
1472
|
+
// import { UserDataExchangeService } from '@dataclouder/ngx-agent-cards';
|
|
1467
1473
|
class LessonUtilsService {
|
|
1468
1474
|
constructor() {
|
|
1469
1475
|
this.#lessonService = inject(LESSONS_TOKEN);
|
|
@@ -1486,15 +1492,6 @@ class LessonUtilsService {
|
|
|
1486
1492
|
}
|
|
1487
1493
|
// Placeholder logic - adapt as needed from original component
|
|
1488
1494
|
console.log('Validating audios for lesson:', lesson.id);
|
|
1489
|
-
// Original logic commented out - requires SpeakerCompConfiguration type
|
|
1490
|
-
// const needsGeneration = lesson.components.some((component: any) => // Use 'any' or define SpeakerCompConfiguration
|
|
1491
|
-
// component.component === 'speaker' && component.settings?.voice && !component.audio
|
|
1492
|
-
// );
|
|
1493
|
-
// if (needsGeneration) {
|
|
1494
|
-
// this.#toastService.warn({ title: 'Audios por generar', subtitle: 'Se encontraron audios pendientes' });
|
|
1495
|
-
// // Consider calling generation service here or returning a flag
|
|
1496
|
-
// // this.#lessonService.generateAudiosForLesson(lesson.id).then(res => { ... });
|
|
1497
|
-
// }
|
|
1498
1495
|
}
|
|
1499
1496
|
/**
|
|
1500
1497
|
* Updates the lesson signal with a new cover image.
|
|
@@ -1544,7 +1541,9 @@ class LessonUtilsService {
|
|
|
1544
1541
|
});
|
|
1545
1542
|
// Note: Saving the lesson should be triggered from the component after calling this.
|
|
1546
1543
|
}
|
|
1544
|
+
// I want to deprecate this method
|
|
1547
1545
|
/**
|
|
1546
|
+
* @deprecated Use create new method.
|
|
1548
1547
|
* Generates lesson content using AI. Assumes the lesson is already saved.
|
|
1549
1548
|
* @param lessonId The ID of the lesson to generate content for.
|
|
1550
1549
|
* @returns The updated lesson object after AI generation, or null on failure.
|
|
@@ -1589,18 +1588,10 @@ class LessonUtilsService {
|
|
|
1589
1588
|
}
|
|
1590
1589
|
try {
|
|
1591
1590
|
this.loadingBarService.showIndeterminate();
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
}
|
|
1597
|
-
const appTask = { task: task };
|
|
1598
|
-
const messages = [
|
|
1599
|
-
{ content: agentCard.systemPrompt, role: ChatRole.System },
|
|
1600
|
-
{ content: appTask.task, role: ChatRole.User },
|
|
1601
|
-
];
|
|
1602
|
-
const response = await this.#agentService.callChatCompletion({ messages, model: agentCard.model });
|
|
1603
|
-
let improvedMarkdown = response.content;
|
|
1591
|
+
const textPrompt = this.#lessonService.getPrompts().content(lesson);
|
|
1592
|
+
const messages = [{ content: textPrompt, role: ChatRole.User }];
|
|
1593
|
+
const response = await this.#agentService.callChatCompletion({ messages, model: { provider: 'google' } });
|
|
1594
|
+
let improvedMarkdown = response.content?.trim() ?? null;
|
|
1604
1595
|
// Remove potential markdown fences
|
|
1605
1596
|
const markdownFenceStart = '```markdown\n';
|
|
1606
1597
|
const markdownFenceEnd = '\n```';
|
|
@@ -1649,22 +1640,9 @@ class LessonUtilsService {
|
|
|
1649
1640
|
this.#toastService.warn({ title: 'Texto Vacío', subtitle: 'No se pudo extraer texto útil del contenido de la lección.' });
|
|
1650
1641
|
return null;
|
|
1651
1642
|
}
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1654
|
-
|
|
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
|
|
1643
|
+
const descriptionPrompt = this.#lessonService.getPrompts().description(lesson);
|
|
1644
|
+
const messages = [{ content: descriptionPrompt, role: ChatRole.User }];
|
|
1645
|
+
const response = await this.#agentService.callChatCompletion({ messages, model: { provider: 'google' } });
|
|
1668
1646
|
let generatedDescription = response.content?.trim() ?? null;
|
|
1669
1647
|
// Basic cleanup (remove potential quotes if the AI wraps the response)
|
|
1670
1648
|
if (generatedDescription && generatedDescription.startsWith('"') && generatedDescription.endsWith('"')) {
|
|
@@ -1755,7 +1733,6 @@ class LessonUtilsService {
|
|
|
1755
1733
|
orphanedIds.push(componentId);
|
|
1756
1734
|
}
|
|
1757
1735
|
}
|
|
1758
|
-
// Log a warning if any orphaned components were found
|
|
1759
1736
|
if (orphanedIds.length > 0) {
|
|
1760
1737
|
console.warn(`[LessonUtilsService] Orphaned components detected and will be removed from lesson data (IDs not found in textCoded):`, orphanedIds);
|
|
1761
1738
|
this.#toastService.warn({
|
|
@@ -1823,6 +1800,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1823
1800
|
type: Output
|
|
1824
1801
|
}] } });
|
|
1825
1802
|
|
|
1803
|
+
// TODO @DEPRECATED in favor to lesson appingles prompts. for english.
|
|
1804
|
+
const PROMPTS = {
|
|
1805
|
+
generateLesson: ``,
|
|
1806
|
+
GetImageSuggestion: ``,
|
|
1807
|
+
};
|
|
1808
|
+
const getImageSuggestion = (language, lessonTopic) => `
|
|
1809
|
+
Create a prompt for a visually engaging banner image for a language learning app. The image should:
|
|
1810
|
+
|
|
1811
|
+
- Feature vibrant colors and clean design that draws attention
|
|
1812
|
+
- Include subtle visual elements representing language learning (books, speech bubbles, or writing implements)
|
|
1813
|
+
- Incorporate one or more of the following focal elements:
|
|
1814
|
+
* A friendly animal character (like an owl, fox, or parrot) that could serve as a mascot
|
|
1815
|
+
* A scenic landscape that represents cultural context (like iconic landmarks or natural settings)
|
|
1816
|
+
* Key objects that relate to the specific lesson topics
|
|
1817
|
+
- Have a balanced composition with room for text overlay
|
|
1818
|
+
- Evoke a sense of curiosity and learning
|
|
1819
|
+
- Use a modern, minimalist art style with defined shapes
|
|
1820
|
+
|
|
1821
|
+
The banner relates to lessons about ${lessonTopic}, this lesson is to learn ${language}.
|
|
1822
|
+
|
|
1823
|
+
return only the text for the description, no explanation, no comments, just the prompt to directly generate the image
|
|
1824
|
+
`;
|
|
1825
|
+
const getPromptGenerateLesson = (baseLang, targetLang, lessonTopic, aditionalPrompt) => `
|
|
1826
|
+
Create a prompt for a Lesson for a language learning app.
|
|
1827
|
+
- the topic is ${lessonTopic},
|
|
1828
|
+
- the language to learn is ${targetLang}
|
|
1829
|
+
- the lesson is for beginners
|
|
1830
|
+
- write lesson in ${baseLang}, but show a lot of vocabulary in ${targetLang}
|
|
1831
|
+
- create sections
|
|
1832
|
+
- use markdown to format it, sections and titles
|
|
1833
|
+
- Create exercises for each section
|
|
1834
|
+
|
|
1835
|
+
${aditionalPrompt}
|
|
1836
|
+
`;
|
|
1837
|
+
|
|
1826
1838
|
class DCLessonMetadataEditorComponent {
|
|
1827
1839
|
constructor() {
|
|
1828
1840
|
// Use signal for input lesson data
|
|
@@ -1833,22 +1845,50 @@ class DCLessonMetadataEditorComponent {
|
|
|
1833
1845
|
this.importNotionRequest = new EventEmitter();
|
|
1834
1846
|
this.improveNotionRequest = new EventEmitter();
|
|
1835
1847
|
// Removed generateAIRequest Output as it's handled internally now
|
|
1836
|
-
// Output
|
|
1837
|
-
|
|
1848
|
+
// Output removed as the component now updates the input signal directly.
|
|
1849
|
+
// @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
|
|
1838
1850
|
// Injected Services
|
|
1839
1851
|
this.#lessonUtilsService = inject(LessonUtilsService);
|
|
1840
1852
|
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
1841
1853
|
this.#lessonService = inject(LESSONS_TOKEN);
|
|
1842
1854
|
this.#turndownService = new TurndownService(); // Instantiate TurndownService
|
|
1843
1855
|
}
|
|
1856
|
+
// Removed generateAIRequest Output as it's handled internally now
|
|
1857
|
+
// Output removed as the component now updates the input signal directly.
|
|
1858
|
+
// @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
|
|
1844
1859
|
// Injected Services
|
|
1845
1860
|
#lessonUtilsService;
|
|
1846
1861
|
#toastService;
|
|
1847
1862
|
#lessonService;
|
|
1848
1863
|
#turndownService; // Instantiate TurndownService
|
|
1849
|
-
// Method to
|
|
1850
|
-
|
|
1851
|
-
this.
|
|
1864
|
+
// Method to handle property changes for ROOT properties (e.g., level)
|
|
1865
|
+
onRootPropertyChange(property, value) {
|
|
1866
|
+
this.lesson.update((current) => {
|
|
1867
|
+
if (!current)
|
|
1868
|
+
return undefined;
|
|
1869
|
+
// Avoid updating metadata directly here
|
|
1870
|
+
if (property === 'metadata') {
|
|
1871
|
+
console.warn('Direct metadata updates should use onMetadataPropertyChange');
|
|
1872
|
+
return current;
|
|
1873
|
+
}
|
|
1874
|
+
return { ...current, [property]: value };
|
|
1875
|
+
});
|
|
1876
|
+
// Emit removed - signal is updated directly above.
|
|
1877
|
+
// this.propertyChange.emit({ propertyPath: property, value });
|
|
1878
|
+
}
|
|
1879
|
+
// Method to handle property changes for METADATA properties
|
|
1880
|
+
onMetadataPropertyChange(property, value) {
|
|
1881
|
+
this.lesson.update((current) => {
|
|
1882
|
+
if (!current)
|
|
1883
|
+
return undefined;
|
|
1884
|
+
// Ensure metadata object exists and update the specific property
|
|
1885
|
+
// Note: Ensure ContentMetadata interface is imported or defined locally if not already.
|
|
1886
|
+
// Assuming ContentMetadata is imported from '../../lesson-mini-components/components/lessons.clases'
|
|
1887
|
+
const updatedMetadata = { ...(current.metadata ?? {}), [property]: value };
|
|
1888
|
+
return { ...current, metadata: updatedMetadata };
|
|
1889
|
+
});
|
|
1890
|
+
// Emit removed - signal is updated directly above.
|
|
1891
|
+
// this.propertyChange.emit({ propertyPath: `metadata.${String(property)}`, value });
|
|
1852
1892
|
}
|
|
1853
1893
|
// Methods to emit action requests
|
|
1854
1894
|
emitSaveRequest() {
|
|
@@ -1865,48 +1905,51 @@ class DCLessonMetadataEditorComponent {
|
|
|
1865
1905
|
* Moved from DCLessonEditorComponent.
|
|
1866
1906
|
*/
|
|
1867
1907
|
async generateByAI() {
|
|
1868
|
-
const
|
|
1869
|
-
if (!
|
|
1908
|
+
const currentLesson = this.lesson(); // Get current value
|
|
1909
|
+
if (!currentLesson?.id) {
|
|
1870
1910
|
this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
|
|
1871
1911
|
return;
|
|
1872
1912
|
}
|
|
1873
1913
|
this.isLoadingLesson.set(true);
|
|
1874
1914
|
try {
|
|
1875
|
-
|
|
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 || '';
|
|
1915
|
+
const rawHtmlContent = currentLesson.textCoded || '';
|
|
1887
1916
|
if (!rawHtmlContent) {
|
|
1888
|
-
console.warn('No HTML content found in lesson to process.');
|
|
1889
|
-
this.
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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.' });
|
|
1917
|
+
console.warn('No HTML content found in lesson to process. taking just description');
|
|
1918
|
+
this.#toastService.info({ title: 'Contenido lección desde 0', subtitle: 'Solo se usará el prompt' });
|
|
1919
|
+
// this.us
|
|
1920
|
+
// Use metadata description, provide empty string if metadata or description is missing
|
|
1921
|
+
const prompt = getPromptGenerateLesson('portugues', 'espanhol', currentLesson.metadata?.description ?? '', '');
|
|
1922
|
+
debugger;
|
|
1923
|
+
await this.#lessonUtilsService.generateByAI(currentLesson.id);
|
|
1906
1924
|
}
|
|
1907
1925
|
else {
|
|
1908
|
-
//
|
|
1909
|
-
|
|
1926
|
+
// Clean orphaned and Save before Improve
|
|
1927
|
+
const lessonToSave = this.#lessonUtilsService.cleanOrphanedComponents(currentLesson);
|
|
1928
|
+
const savedLesson = await this.#lessonService.postLesson(lessonToSave);
|
|
1929
|
+
if (!savedLesson) {
|
|
1930
|
+
this.#toastService.error({ title: 'Error al guardar', subtitle: 'No se pudo guardar antes de generar con IA.' });
|
|
1931
|
+
throw new Error('Failed to save before AI generation');
|
|
1932
|
+
}
|
|
1933
|
+
this.lesson.set(savedLesson);
|
|
1934
|
+
// Replace encoded JSON with actual text before Markdown conversion
|
|
1935
|
+
const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
|
|
1936
|
+
// Convert the processed HTML (with text instead of JSON) to Markdown
|
|
1937
|
+
const markdownText = this.#turndownService.turndown(processedHtmlContent);
|
|
1938
|
+
// Use the updated lesson signal value for AI improvement
|
|
1939
|
+
const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), markdownText);
|
|
1940
|
+
if (improvedMarkdown) {
|
|
1941
|
+
// Convert the improved Markdown back to HTML before setting it
|
|
1942
|
+
const improvedHtml = marked(improvedMarkdown);
|
|
1943
|
+
// Update the signal directly
|
|
1944
|
+
this.lesson.update((current) => (current ? { ...current, textCoded: improvedHtml } : undefined));
|
|
1945
|
+
// Save the AI-generated content
|
|
1946
|
+
await this.#lessonService.postLesson(this.lesson());
|
|
1947
|
+
this.#toastService.success({ title: 'Contenido generado', subtitle: 'Se generó y guardó el contenido con IA.' });
|
|
1948
|
+
}
|
|
1949
|
+
else {
|
|
1950
|
+
// Toast error is handled by the service
|
|
1951
|
+
throw new Error('AI generation failed or lesson fetch failed after generation.');
|
|
1952
|
+
}
|
|
1910
1953
|
}
|
|
1911
1954
|
}
|
|
1912
1955
|
catch (error) {
|
|
@@ -1952,13 +1995,12 @@ class DCLessonMetadataEditorComponent {
|
|
|
1952
1995
|
}
|
|
1953
1996
|
const generatedDescription = await this.#lessonUtilsService.generateDescriptionWithAI(currentLesson);
|
|
1954
1997
|
if (generatedDescription) {
|
|
1955
|
-
// Use the
|
|
1956
|
-
this.
|
|
1957
|
-
// this.emitSaveRequest(); // optional do something to automatically save
|
|
1998
|
+
// Use the new method for metadata properties
|
|
1999
|
+
this.onMetadataPropertyChange('description', generatedDescription);
|
|
1958
2000
|
}
|
|
1959
2001
|
}
|
|
1960
2002
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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"
|
|
2003
|
+
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" }, 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.metadata?.title\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.description\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.prompt\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.isPublished\"\n (ngModelChange)=\"onMetadataPropertyChange('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)=\"onRootPropertyChange('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
|
|
1962
2004
|
FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
|
|
1963
2005
|
LangDescTranslationPipe, name: "langDesc" }, { kind: "ngmodule", type: // Added Pipe
|
|
1964
2006
|
InputGroupModule }, { kind: "component", type: i5$1.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["style", "styleClass"] }] }); }
|
|
@@ -1974,7 +2016,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1974
2016
|
FlagLanguagePipe, // Added Pipe
|
|
1975
2017
|
LangDescTranslationPipe, // Added Pipe
|
|
1976
2018
|
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)=\"
|
|
2019
|
+
], 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.metadata?.title\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.description\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.prompt\"\n (ngModelChange)=\"onMetadataPropertyChange('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.metadata?.isPublished\"\n (ngModelChange)=\"onMetadataPropertyChange('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)=\"onRootPropertyChange('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" }]
|
|
1978
2020
|
}], propDecorators: { lesson: [{
|
|
1979
2021
|
type: Input,
|
|
1980
2022
|
args: [{ required: true }]
|
|
@@ -1987,10 +2029,112 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1987
2029
|
type: Output
|
|
1988
2030
|
}], improveNotionRequest: [{
|
|
1989
2031
|
type: Output
|
|
1990
|
-
}], propertyChange: [{
|
|
1991
|
-
type: Output
|
|
1992
2032
|
}] } });
|
|
1993
2033
|
|
|
2034
|
+
// Define placeholder endpoints - these should be configured appropriately
|
|
2035
|
+
const LESSONS_BASE_PATH = 'api/lesson'; // Example base path
|
|
2036
|
+
class DefaultLessonsService {
|
|
2037
|
+
constructor() {
|
|
2038
|
+
this.httpCoreService = inject(HttpCoreService);
|
|
2039
|
+
// --- Endpoint Definitions (Hardcoded as requested) ---
|
|
2040
|
+
this.endpoints = {
|
|
2041
|
+
queryLessons: `${LESSONS_BASE_PATH}/query`,
|
|
2042
|
+
getLesson: (id) => `${LESSONS_BASE_PATH}/${id}`,
|
|
2043
|
+
saveLesson: `api/user/saveLesson`,
|
|
2044
|
+
updateLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming PUT to /lessons/:id
|
|
2045
|
+
deleteLesson: (id) => `${LESSONS_BASE_PATH}/${id}`, // Assuming DELETE to /lessons/:id
|
|
2046
|
+
generateLesson: `${LESSONS_BASE_PATH}/generate`, // Placeholder
|
|
2047
|
+
generateByAI: `${LESSONS_BASE_PATH}/generate-ai`, // Placeholder
|
|
2048
|
+
improveMDWithAI: `${LESSONS_BASE_PATH}/improve-markdown-ai`, // Placeholder
|
|
2049
|
+
QueryLessons: 'api/lesson/query',
|
|
2050
|
+
Lesson: 'api/lesson',
|
|
2051
|
+
SaveLesson: 'api/lesson-polilan',
|
|
2052
|
+
GetPublicLessons: 'api/lesson/publicLessons',
|
|
2053
|
+
GetUnpublishedLessons: 'api/lesson/unpublished',
|
|
2054
|
+
TakenLesson: 'api/lesson/taken',
|
|
2055
|
+
DeleteLesson: 'api/lesson',
|
|
2056
|
+
Base: 'api/lesson',
|
|
2057
|
+
GenerateBanner: 'api/lesson/generate-banner',
|
|
2058
|
+
};
|
|
2059
|
+
}
|
|
2060
|
+
saveTakenLesson(lesson) {
|
|
2061
|
+
// Not sure how to implement this yet.
|
|
2062
|
+
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2063
|
+
}
|
|
2064
|
+
// --- Method Implementations ---
|
|
2065
|
+
async getLessons(paginator) {
|
|
2066
|
+
// Assuming paginator is the body for a POST request based on the example
|
|
2067
|
+
return this.httpCoreService.post(this.endpoints.queryLessons, paginator || {});
|
|
2068
|
+
}
|
|
2069
|
+
async getLesson(id) {
|
|
2070
|
+
return this.httpCoreService.get(this.endpoints.getLesson(id));
|
|
2071
|
+
}
|
|
2072
|
+
async postLesson(lesson) {
|
|
2073
|
+
return this.httpCoreService.post(this.endpoints.saveLesson, lesson);
|
|
2074
|
+
}
|
|
2075
|
+
async updateLesson(lesson) {
|
|
2076
|
+
if (!lesson._id) {
|
|
2077
|
+
throw new Error('Lesson ID is required for update.');
|
|
2078
|
+
}
|
|
2079
|
+
// Assuming _id is the identifier
|
|
2080
|
+
return this.httpCoreService.put(this.endpoints.updateLesson(lesson._id), lesson);
|
|
2081
|
+
}
|
|
2082
|
+
async deleteLesson(id) {
|
|
2083
|
+
return this.httpCoreService.delete(this.endpoints.deleteLesson(id));
|
|
2084
|
+
}
|
|
2085
|
+
async generateLesson(lesson) {
|
|
2086
|
+
// This endpoint might need specific data or structure
|
|
2087
|
+
return this.httpCoreService.post(this.endpoints.generateLesson, lesson);
|
|
2088
|
+
}
|
|
2089
|
+
async postGenerateByAI(id) {
|
|
2090
|
+
return this.httpCoreService.post(this.endpoints.generateByAI, { id });
|
|
2091
|
+
}
|
|
2092
|
+
async postImproveMDWithAI(lessonId, markdownText) {
|
|
2093
|
+
return this.httpCoreService.post(this.endpoints.improveMDWithAI, { id: lessonId, markdown: markdownText });
|
|
2094
|
+
}
|
|
2095
|
+
extractTextFromHtml(html) {
|
|
2096
|
+
// Copied from src/app/core/data-services/lessons.service.ts
|
|
2097
|
+
const r1 = new RegExp('~(.+?)~', 'g');
|
|
2098
|
+
const lessonHtml = html.replace(r1, (_matching, jsonCoded) => {
|
|
2099
|
+
try {
|
|
2100
|
+
const data = JSON.parse(jsonCoded);
|
|
2101
|
+
return `<span>${data?.settings?.text || ''}</span>`; // Added default empty string
|
|
2102
|
+
}
|
|
2103
|
+
catch (e) {
|
|
2104
|
+
console.error('Error parsing JSON in extractTextFromHtml:', jsonCoded, e);
|
|
2105
|
+
return ''; // Return empty string on error
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
// Remove HTML tags
|
|
2109
|
+
let text = lessonHtml.replace(/<[^>]*>/g, ' ');
|
|
2110
|
+
// Remove style and script content
|
|
2111
|
+
text = text.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, ' ');
|
|
2112
|
+
text = text.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, ' ');
|
|
2113
|
+
// Decode HTML entities
|
|
2114
|
+
text = text.replace(/ /g, ' ');
|
|
2115
|
+
text = text.replace(/&/g, '&');
|
|
2116
|
+
text = text.replace(/</g, '<');
|
|
2117
|
+
text = text.replace(/>/g, '>');
|
|
2118
|
+
// Remove extra whitespace
|
|
2119
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
2120
|
+
return text;
|
|
2121
|
+
}
|
|
2122
|
+
generateBanner(prompt, lessonId) {
|
|
2123
|
+
return this.httpCoreService.post(this.endpoints.GenerateBanner, { prompt, lessonId });
|
|
2124
|
+
}
|
|
2125
|
+
getPrompts() {
|
|
2126
|
+
return null;
|
|
2127
|
+
}
|
|
2128
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2129
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, providedIn: 'root' }); }
|
|
2130
|
+
}
|
|
2131
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultLessonsService, decorators: [{
|
|
2132
|
+
type: Injectable,
|
|
2133
|
+
args: [{
|
|
2134
|
+
providedIn: 'root',
|
|
2135
|
+
}]
|
|
2136
|
+
}] });
|
|
2137
|
+
|
|
1994
2138
|
class DCLessonEditorComponent {
|
|
1995
2139
|
// Services
|
|
1996
2140
|
#activatedRoute; // Re-inject as it's needed for navigation
|
|
@@ -1999,6 +2143,7 @@ class DCLessonEditorComponent {
|
|
|
1999
2143
|
#router;
|
|
2000
2144
|
#lessonService;
|
|
2001
2145
|
#toastService;
|
|
2146
|
+
#loadingBarService;
|
|
2002
2147
|
constructor() {
|
|
2003
2148
|
// Services
|
|
2004
2149
|
this.#activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
|
|
@@ -2007,15 +2152,31 @@ class DCLessonEditorComponent {
|
|
|
2007
2152
|
this.#router = inject(Router);
|
|
2008
2153
|
this.#lessonService = inject(LESSONS_TOKEN);
|
|
2009
2154
|
this.#toastService = inject(TOAST_ALERTS_TOKEN);
|
|
2155
|
+
this.#loadingBarService = inject(LoadingBarService);
|
|
2156
|
+
this.defaultLessonsService = inject(DefaultLessonsService);
|
|
2157
|
+
this.promptService = inject(PromptService);
|
|
2158
|
+
this.agentChatService = inject(CONVERSATION_AI_TOKEN);
|
|
2159
|
+
this.ngxVertexService = inject(NgxVertexService);
|
|
2160
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2010
2161
|
// Signals States
|
|
2011
2162
|
this.lessonId = toSignal(inject(ActivatedRoute).paramMap.pipe(map((params) => params.get('id'))));
|
|
2012
2163
|
this.lesson = signal(undefined); // Initialize as undefined
|
|
2013
2164
|
this.isLoadingLesson = signal(false);
|
|
2014
2165
|
// Computed Signals
|
|
2015
2166
|
this.coverImageUrl = computed(() => {
|
|
2167
|
+
// Priority Order 1 Metadata Banner, 2 Banner, 3 Media First Images, 4 Default Banner TODO: reveme banner after migration to Content
|
|
2016
2168
|
const currentLesson = this.lesson();
|
|
2017
|
-
|
|
2018
|
-
|
|
2169
|
+
if (currentLesson?.metadata?.banner?.url) {
|
|
2170
|
+
return currentLesson.metadata.banner.url;
|
|
2171
|
+
}
|
|
2172
|
+
else if (currentLesson?.banner?.url) {
|
|
2173
|
+
return currentLesson.banner.url;
|
|
2174
|
+
}
|
|
2175
|
+
else if (currentLesson?.media?.images?.find((img) => img.type === 'cover')) {
|
|
2176
|
+
// 3 Media First Images
|
|
2177
|
+
return currentLesson.media.images.find((img) => img.type === 'cover')?.url;
|
|
2178
|
+
}
|
|
2179
|
+
return '/assets/images/default_banner.webp';
|
|
2019
2180
|
});
|
|
2020
2181
|
// Computed signal to get dynamic components as an array for easier iteration in the template
|
|
2021
2182
|
this.dynamicComponentsArray = computed(() => {
|
|
@@ -2034,6 +2195,7 @@ class DCLessonEditorComponent {
|
|
|
2034
2195
|
fileName: 'cover',
|
|
2035
2196
|
cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
|
|
2036
2197
|
};
|
|
2198
|
+
this.promptsVisible = false;
|
|
2037
2199
|
// Effect to fetch lesson data when ID changes
|
|
2038
2200
|
effect(async () => {
|
|
2039
2201
|
const id = this.lessonId();
|
|
@@ -2157,15 +2319,6 @@ class DCLessonEditorComponent {
|
|
|
2157
2319
|
const newComponent = result?.obj;
|
|
2158
2320
|
if (newComponent?.id) {
|
|
2159
2321
|
console.log('Component builder closed, result received in editor:', newComponent);
|
|
2160
|
-
// // Transform LessonComponentConfiguration to DynamicContentComponent
|
|
2161
|
-
// const dynamicComponentToAdd: DynamicContentComponent = {
|
|
2162
|
-
// id: componentConfig.id,
|
|
2163
|
-
// component: componentConfig.component,
|
|
2164
|
-
// inputs: {
|
|
2165
|
-
// config: componentConfig, // Pass the original config object as an input named 'config'
|
|
2166
|
-
// // Add other potential inputs if needed based on component type later
|
|
2167
|
-
// },
|
|
2168
|
-
// };
|
|
2169
2322
|
// Update the lesson signal, adding the transformed component to the dynamicComponents object
|
|
2170
2323
|
this.lesson.update((currentLesson) => {
|
|
2171
2324
|
if (!currentLesson)
|
|
@@ -2228,9 +2381,51 @@ class DCLessonEditorComponent {
|
|
|
2228
2381
|
showComponentDetails(data) {
|
|
2229
2382
|
alert('showComponentDetails' + JSON.stringify(data));
|
|
2230
2383
|
}
|
|
2384
|
+
async generateBanner() {
|
|
2385
|
+
this.#toastService.info({ title: 'Generando prompt de sugerencia', subtitle: 'Por favor, espera' });
|
|
2386
|
+
const prompt = getImageSuggestion('portugues', this.lesson().description);
|
|
2387
|
+
const geminiRes = await this.ngxVertexService.generateText([{ role: ChatRoleVertex.User, content: prompt }]);
|
|
2388
|
+
this.promptService
|
|
2389
|
+
.openPrompt({
|
|
2390
|
+
title: 'Generar Banner',
|
|
2391
|
+
initialValue: geminiRes.content || '',
|
|
2392
|
+
message: 'Ingresa una idea de lo que quieres ver',
|
|
2393
|
+
acceptText: 'Generar',
|
|
2394
|
+
cancelText: 'Cancelar',
|
|
2395
|
+
})
|
|
2396
|
+
.then((promptResult) => {
|
|
2397
|
+
if (promptResult) {
|
|
2398
|
+
this.#loadingBarService.showIndeterminate();
|
|
2399
|
+
this.defaultLessonsService.generateBanner(promptResult, this.lessonId()).then((result) => {
|
|
2400
|
+
if (result) {
|
|
2401
|
+
this.updateLessonProperty('banner', result.banner);
|
|
2402
|
+
}
|
|
2403
|
+
this.#loadingBarService.successAndHide();
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
});
|
|
2407
|
+
// const imagePrompt = prompt('alguna idea de lo que quieres ver?');
|
|
2408
|
+
// this.#loadingBarService.showIndeterminate();
|
|
2409
|
+
// const result = await this.defaultLessonsService.generateBanner(imagePrompt, this.lessonId());
|
|
2410
|
+
// if (result) {
|
|
2411
|
+
// this.updateLessonProperty('banner', result.banner);
|
|
2412
|
+
// }
|
|
2413
|
+
// console.log('Generated banner:', result);
|
|
2414
|
+
//
|
|
2415
|
+
// this.#loadingBarService.successAndHide();
|
|
2416
|
+
}
|
|
2417
|
+
showPrompts() {
|
|
2418
|
+
this.promptsVisible = true;
|
|
2419
|
+
const promptsFn = this.#lessonService.getPrompts();
|
|
2420
|
+
this.prompts = { banner: promptsFn.banner(this.lesson()), content: promptsFn.content(this.lesson()), description: promptsFn.description(this.lesson()) };
|
|
2421
|
+
debugger;
|
|
2422
|
+
console.log(this.prompts);
|
|
2423
|
+
this.cdr.markForCheck();
|
|
2424
|
+
}
|
|
2231
2425
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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()\"
|
|
2233
|
-
DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "
|
|
2426
|
+
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\n <p-button\n (click)=\"generateBanner()\"\n class=\"generate-banner-btn\"\n icon=\"pi pi-sparkles\"\n severity=\"primary\"\n [rounded]=\"true\"\n [raised]=\"true\"\n pTooltip=\"Generar Banner AI\"\n tooltipPosition=\"left\"></p-button>\n\n <p-button class=\"prompt-visual\" icon=\"pi pi-info\" label=\"Ver Prompts\" [link]=\"true\" (click)=\"showPrompts()\" />\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</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\n<div>\n <h4>Aqui estan los prompts</h4>\n</div>\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}\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
|
|
2427
|
+
DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest"] }, { kind: "ngmodule", type: // Add the metadata editor here
|
|
2428
|
+
DialogModule }, { kind: "component", type: i7.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }] }); }
|
|
2234
2429
|
}
|
|
2235
2430
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
|
|
2236
2431
|
type: Component,
|
|
@@ -2245,7 +2440,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2245
2440
|
TooltipModule,
|
|
2246
2441
|
DCLessonComponentAdderComponent, // Add the component adder here
|
|
2247
2442
|
DCLessonMetadataEditorComponent, // Add the metadata editor here
|
|
2248
|
-
|
|
2443
|
+
DialogModule,
|
|
2444
|
+
], 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\n <p-button\n (click)=\"generateBanner()\"\n class=\"generate-banner-btn\"\n icon=\"pi pi-sparkles\"\n severity=\"primary\"\n [rounded]=\"true\"\n [raised]=\"true\"\n pTooltip=\"Generar Banner AI\"\n tooltipPosition=\"left\"></p-button>\n\n <p-button class=\"prompt-visual\" icon=\"pi pi-info\" label=\"Ver Prompts\" [link]=\"true\" (click)=\"showPrompts()\" />\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</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\n<div>\n <h4>Aqui estan los prompts</h4>\n</div>\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}\n"] }]
|
|
2249
2445
|
}], ctorParameters: () => [], propDecorators: { target: [{
|
|
2250
2446
|
type: ViewChild,
|
|
2251
2447
|
args: ['target', { read: ViewContainerRef }]
|
|
@@ -2254,103 +2450,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2254
2450
|
args: ['dhtml', { static: true }]
|
|
2255
2451
|
}] } });
|
|
2256
2452
|
|
|
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
|
-
|
|
2354
2453
|
// This is the base class for all the components that are going to be used in the lessons.
|
|
2355
2454
|
class LessonDynamicComponent {
|
|
2356
2455
|
constructor() {
|
|
@@ -2378,5 +2477,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2378
2477
|
* Generated bundle index. Do not edit.
|
|
2379
2478
|
*/
|
|
2380
2479
|
|
|
2381
|
-
export {
|
|
2480
|
+
export { 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, getLanguageSimpleAgent, getLessonComponentClass, provideLessonsService, provideNotionService };
|
|
2382
2481
|
//# sourceMappingURL=dataclouder-ngx-lessons.mjs.map
|