@dataclouder/ngx-lessons 0.0.34 → 0.1.0

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.
@@ -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 EnglishLessonSkill = `
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 an English lesson professor, you write lessons for Spanish learners.
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 an English lesson for Spanish learners at a basic level (A1-A2).
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 Spanish translations.
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 an English lesson for Spanish learners at a medium level (B1-B2).
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 an English lesson for Spanish learners at an advanced level (C1-C2).
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 UserRequirements = `
614
- User also can provide additional instructions or requirements for the lesson or current lesson to improve it.
615
- `;
616
- const BasicAgentCard$1 = {
617
- systemPrompt: `
618
- ${MarkdownWriterSkill}
619
- ${EnglishLessonSkill}
620
- ${UserRequirements}
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
- if (this.lesson?.media?.images[0]?.url) {
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=\"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"] }] }); }
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=\"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"] }]
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: null, score: 100, status: 'finished', lastAccess: new Date() };
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 agentCard = BasicAgentCard$1;
1593
- let task = `Improve the following lesson return only the lesson markdown: ${markdownText}`;
1594
- if (lesson.prompt) {
1595
- task = `${task} Also consider the original prompt: ${lesson.prompt} when improving the lesson.`;
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 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
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 for property changes
1837
- this.propertyChange = new EventEmitter(); // To update parent signal
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 emit property changes, called by ngModelChange in the template
1850
- onPropertyChange(property, value) {
1851
- this.propertyChange.emit({ property, value });
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 currentLessonValue = this.lesson(); // Get current value
1869
- if (!currentLessonValue?.id) {
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
- // 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 || '';
1915
+ const rawHtmlContent = currentLesson.textCoded || '';
1887
1916
  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.' });
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
- // Toast error is handled by the service
1909
- throw new Error('AI generation failed or lesson fetch failed after generation.');
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 existing propertyChange emitter to update the parent signal
1956
- this.onPropertyChange('description', generatedDescription);
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", 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
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)=\"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" }]
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(/&nbsp;/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
- const coverImage = currentLesson?.media?.images?.find((img) => img.type === 'cover');
2018
- return coverImage?.url || '/assets/images/default_banner.webp';
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()\"\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"] }] }); }
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
- ], 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"] }]
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(/&nbsp;/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 { 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 };
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