@dataclouder/ngx-lessons 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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';
@@ -20,9 +20,11 @@ import { PaginatorModule } from 'primeng/paginator';
20
20
  import { DatePipe, NgComponentOutlet, KeyValuePipe, CommonModule } from '@angular/common';
21
21
  import * as i1$2 from '@angular/router';
22
22
  import { RouterModule, ActivatedRoute, Router } from '@angular/router';
23
- import * as i4$1 from '@dataclouder/ngx-core';
24
- import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService } from '@dataclouder/ngx-core';
23
+ import * as i4$2 from '@dataclouder/ngx-core';
24
+ import { PaginationBase, TOAST_ALERTS_TOKEN, DCFilterBarComponent, QuickTableComponent, LoadingBarService, HttpCoreService, PromptService } from '@dataclouder/ngx-core';
25
25
  import { PopoverModule } from 'primeng/popover';
26
+ import * as i4$1 from 'primeng/tag';
27
+ import { TagModule } from 'primeng/tag';
26
28
  import * as i2$2 from 'primeng/speeddial';
27
29
  import { SpeedDialModule } from 'primeng/speeddial';
28
30
  import * as i3 from 'primeng/card';
@@ -32,16 +34,21 @@ import { map } from 'rxjs';
32
34
  import BalloonEditor from '@ckeditor/ckeditor5-build-balloon-block';
33
35
  import * as i3$1 from '@ckeditor/ckeditor5-angular';
34
36
  import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
35
- import * as i5$1 from 'primeng/splitter';
37
+ import * as i5$2 from 'primeng/splitter';
36
38
  import { SplitterModule } from 'primeng/splitter';
37
39
  import * as i2$5 from 'primeng/tooltip';
38
40
  import { TooltipModule } from 'primeng/tooltip';
39
41
  import { ResolutionType, AspectType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
40
- import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
42
+ import { TextEngines, ConversationType, USER_DATA_EXCHANGE, ChatRole, EvalResultStringDefinition, DCChatComponent, CONVERSATION_AI_TOKEN } from '@dataclouder/ngx-agent-cards';
41
43
  import * as i2$4 from 'primeng/drawer';
42
44
  import { DrawerModule } from 'primeng/drawer';
45
+ import * as i7 from 'primeng/dialog';
46
+ import { DialogModule } from 'primeng/dialog';
43
47
  import TurndownService from 'turndown';
44
48
  import { marked } from 'marked';
49
+ import * as i5$1 from 'primeng/inputgroup';
50
+ import { InputGroupModule } from 'primeng/inputgroup';
51
+ import { NgxVertexService, ChatRoleVertex } from '@dataclouder/ngx-vertex';
45
52
  import * as i2$6 from 'primeng/api';
46
53
 
47
54
  class ComponentBuilder {
@@ -420,7 +427,6 @@ function getLessonComponentClass(type) {
420
427
  // import { UntypedFormControl } from '@angular/forms';
421
428
  // import { LessonComponents } from '../models/lessons.class';
422
429
  const LESSONS_TOKEN = new InjectionToken('Lessons Service');
423
- // Think on someway of getting the toast alerts service
424
430
  class LessonsAbstractService {
425
431
  }
426
432
  // my-service.provider.ts
@@ -570,23 +576,26 @@ You incorporate visual dividers to separate major content sections
570
576
 
571
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.
572
578
  `;
573
- 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) => `
574
583
 
575
- You are also an English lesson professor, you write lessons for Spanish learners.
576
- 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.
577
586
 
578
587
  You can infer the level of the lesson from the content or user request, so can increase difficulty
579
588
 
580
- 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).
581
590
 
582
591
  Include:
583
592
 
584
593
  - Simple dialogues or short texts.
585
- - Key vocabulary with simple definitions or Spanish translations.
594
+ - Key vocabulary with simple definitions or ${langBase} translations.
586
595
  - Basic grammar points explained simply (e.g., "to be" verb, simple present).
587
596
  - Practice exercises (e.g., fill-in-the-blanks, simple questions).
588
597
 
589
- 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).
590
599
 
591
600
  Include:
592
601
 
@@ -596,7 +605,7 @@ Include:
596
605
  - Exercises that require more detailed responses or understanding.
597
606
  - The content should be engaging and cover topics relevant to daily life, work, or hobbies.
598
607
 
599
- 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).
600
609
 
601
610
  Include:
602
611
 
@@ -605,19 +614,17 @@ Include:
605
614
  - Exploration of complex grammar or stylistic points.
606
615
  - Exercises that encourage critical thinking, debate, or detailed writing.
607
616
  - The content should be stimulating and cover a wide range of topics, including academic or professional contexts.
608
-
609
- `;
610
- const UserRequirements = `
611
- User also can provide additional instructions or requirements for the lesson or current lesson to improve it.
612
617
  `;
613
- const BasicAgentCard$1 = {
614
- systemPrompt: `
615
- ${MarkdownWriterSkill}
616
- ${EnglishLessonSkill}
617
- ${UserRequirements}
618
-
619
- `,
620
- 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
+ };
621
628
  };
622
629
 
623
630
  var EventCard;
@@ -630,6 +637,7 @@ var EventCard;
630
637
  class DcLessonCardComponent {
631
638
  constructor() {
632
639
  this.showOptions = true;
640
+ this.cardHeight = '200px';
633
641
  this.onAction = new EventEmitter();
634
642
  this.coverUrl = 'assets/background/default-background.webp';
635
643
  this.eventType = EventCard;
@@ -658,9 +666,8 @@ class DcLessonCardComponent {
658
666
  ];
659
667
  }
660
668
  ngOnInit() {
661
- if (this.lesson?.media?.images[0]?.url) {
662
- this.coverUrl = this.lesson.media.images[0].url;
663
- }
669
+ console.log(this.lesson);
670
+ this.coverUrl = this.lesson?.banner?.url || this.lesson?.media?.images?.[0]?.url || 'assets/background/default-background.webp';
664
671
  }
665
672
  eventCard(eventType) {
666
673
  switch (eventType) {
@@ -679,15 +686,17 @@ class DcLessonCardComponent {
679
686
  }
680
687
  }
681
688
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
682
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcLessonCardComponent, isStandalone: true, selector: "dc-lesson-card", inputs: { lesson: "lesson", showOptions: "showOptions" }, outputs: { onAction: "onAction" }, ngImport: i0, template: "<div class=\"card-container\">\n @if(showOptions){\n <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 <span class=\"status-tag success\">Tomada</span>\n }\n @if (lesson.taken?.status == 'failed') {\n <span class=\"status-tag danger\">Fallida</span>\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"] }] }); }
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"] }] }); }
683
690
  }
684
691
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcLessonCardComponent, decorators: [{
685
692
  type: Component,
686
- args: [{ selector: 'dc-lesson-card', standalone: true, imports: [DatePipe, ButtonModule, PopoverModule, SpeedDialModule, CardModule], template: "<div class=\"card-container\">\n @if(showOptions){\n <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 <span class=\"status-tag success\">Tomada</span>\n }\n @if (lesson.taken?.status == 'failed') {\n <span class=\"status-tag danger\">Fallida</span>\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"] }]
687
694
  }], propDecorators: { lesson: [{
688
695
  type: Input
689
696
  }], showOptions: [{
690
697
  type: Input
698
+ }], cardHeight: [{
699
+ type: Input
691
700
  }], onAction: [{
692
701
  type: Output
693
702
  }] } });
@@ -803,15 +812,15 @@ class DCLessonListComponent extends PaginationBase {
803
812
  }
804
813
  }
805
814
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.Router }, { token: i1$2.ActivatedRoute }, { token: LESSONS_TOKEN }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
806
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { showOptions: "showOptions", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "items", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
815
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonListComponent, isStandalone: true, selector: "dc-lesson-list", inputs: { showOptions: "showOptions", customCardComponent: "customCardComponent", customFilters: "customFilters", viewType: "viewType" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "items", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
807
816
  }
808
817
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonListComponent, decorators: [{
809
818
  type: Component,
810
- args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1rem;flex:1;overflow-y:auto;min-height:0}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"] }]
819
+ args: [{ selector: 'dc-lesson-list', standalone: true, imports: [RouterModule, DCFilterBarComponent, PaginatorModule, NgComponentOutlet, QuickTableComponent], template: "<dc-filter-bar [customFilters]=\"customFilters\" (onFilterAction)=\"applyFilterBarEvent($event)\" (onNew)=\"newLesson()\"></dc-filter-bar>\n\n@if(viewType === 'table') {\n<app-quick-table [columns]=\"columns\" [tableData]=\"lessons\" (onAction)=\"doOrEmitAction($event)\"></app-quick-table>\n} @else {\n<div class=\"lesson-list-container\">\n @if (!isLoadingLessons && lessons?.length > 0) { @for (lesson of lessons; track lesson._id) {\n <ng-container\n #outlet=\"ngComponentOutlet\"\n [ngComponentOutlet]=\"cardComponent\"\n [ngComponentOutletInputs]=\"{\n lesson: lesson,\n showOptions: showOptions\n }\">\n </ng-container>\n } } @else {\n <p>No se encontraron lecciones disponibles</p>\n }\n</div>\n}\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} lecciones\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:flex;flex-direction:column;height:100%}.lesson-list-container{padding:1.5rem;flex:1;overflow-y:auto;min-height:0}@media (max-width: 768px){.lesson-list-container{margin-top:1rem;padding:0rem}}p-paginator{margin-top:auto;padding:.5rem 1rem}\n"] }]
811
820
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.Router }, { type: i1$2.ActivatedRoute }, { type: LessonsAbstractService, decorators: [{
812
821
  type: Inject,
813
822
  args: [LESSONS_TOKEN]
814
- }] }, { type: i4$1.ToastAlertsAbstractService, decorators: [{
823
+ }] }, { type: i4$2.ToastAlertsAbstractService, decorators: [{
815
824
  type: Inject,
816
825
  args: [TOAST_ALERTS_TOKEN]
817
826
  }] }], propDecorators: { showOptions: [{
@@ -959,6 +968,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
959
968
  }]
960
969
  }] });
961
970
 
971
+ const EnglishEvaluationSkill = `
972
+ You are a virtual professor for the Polilan English learning app. Your primary role is to evaluate a student's English conversational performance.
973
+
974
+ You will be provided with two pieces of information for each evaluation:
975
+ 1. **The student's English level:** This will be one of "Beginner", "Intermediate", or "Advanced".
976
+ 2. **A conversation transcript:** This will be the dialogue between the Student and an Assistant, with each turn clearly labeled (e.g., "Student or User: ...", "Assistant: ...").
977
+
978
+ Your sole task is to evaluate the *Student's* contribution to the conversation. **Do NOT evaluate the Assistant's turns.**
979
+
980
+ Evaluate the student's performance based on the following aspects, *always keeping the student's provided level in mind* and adjusting your expectations accordingly specially with beginners give them credits just for trying:
981
+
982
+ * **Grammar & Syntax:** Evaluate accuracy and complexity *relative to the expected level*. (Beginners: focus on basic accuracy; Advanced: expect complex structures with high accuracy).
983
+ * **Vocabulary & Usage:** Evaluate the range and appropriateness of words used *relative to the expected level*. (Beginners: focus on using basic words correctly; Advanced: expect varied and idiomatic language).
984
+ * **Cohesion & Coherence:** Evaluate how well their turns connect and make sense within the conversation *relative to the expected level*. (Beginners: focus on simple, clear responses; Advanced: expect logical flow and detailed contributions).
985
+ * **Task Completion/Relevance:** Evaluate how effectively they participate and respond to the conversation's goals or topics *relative to the expected level*.
986
+
987
+ Assign a rating from 0 to 3 based on how well the student performs *compared to the typical expectations for their provided level*:
988
+
989
+ * **0: Bad** - Performance is significantly below what is expected for this level. Many errors impede communication.
990
+ * **1: Not Bad** - Performance is below expectations for this level, but communication is sometimes possible despite frequent errors.
991
+ * **2: Good** - Performance meets expectations for this level, demonstrating solid understanding and usage with only occasional minor errors.
992
+ * **3: Very Good** - Performance exceeds expectations for this level, demonstrating a strong command and potential to progress quickly.
993
+
994
+ Provide detailed, constructive feedback that explains the rating. Point out specific strengths and areas for improvement based on their performance *at their given level*. Use examples from the dialog if helpful. Maintain a supportive, encouraging, and educational tone appropriate for a virtual professor guiding a student at their specific stage.
995
+ `;
996
+
962
997
  class DCLessonRendererComponent {
963
998
  constructor() {
964
999
  // --- Signal Inputs ---
@@ -971,12 +1006,14 @@ class DCLessonRendererComponent {
971
1006
  this.toastrService = inject(TOAST_ALERTS_TOKEN);
972
1007
  this.lessonService = inject(LESSONS_TOKEN);
973
1008
  this.lessonAIService = inject(LessonAIService); // Inject the new service
1009
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
974
1010
  // --- State Signals ---
975
1011
  this.lesson = signal(undefined); // Internal lesson state signal
976
1012
  this.chatVisible = signal(false); // Signal for chat visibility
977
1013
  this.agentMasterLesson = signal(undefined); // Signal for agent card
978
1014
  this.evaluatorAgentCard = signal(undefined); // Signal for evaluator card
979
1015
  this.conversationSettings = signal(undefined);
1016
+ this.evalAgentTask = signal(undefined); // Signal for evaluator card
980
1017
  // --- Computed Signals ---
981
1018
  this.imageCover = computed(() => this.lesson()?.media?.images?.find((img) => img.type === 'cover')?.url); // Computed signal for imageCover
982
1019
  // --- Properties ---
@@ -1017,6 +1054,12 @@ class DCLessonRendererComponent {
1017
1054
  effect(() => {
1018
1055
  const currentLesson = this.lesson(); // Read the lesson signal
1019
1056
  const newTextCoded = currentLesson?.textCoded; // Get the current textCoded value
1057
+ this.evalAgentTask.set({
1058
+ systemPrompt: EnglishEvaluationSkill,
1059
+ model: { provider: 'google' },
1060
+ task: `Please evaluate the user conversation student data is: \n ${this.userDataExchange.getUserDataInformation()}`,
1061
+ expectedResponseType: EvalResultStringDefinition,
1062
+ });
1020
1063
  // Quick fix to only render on changes textCode not all the lesson
1021
1064
  if (newTextCoded !== this.previousTextCoded) {
1022
1065
  if (newTextCoded !== undefined && newTextCoded !== null && currentLesson) {
@@ -1190,7 +1233,7 @@ class DCLessonRendererComponent {
1190
1233
  this.toastrService.error({ subtitle: 'Cannot save result, lesson data missing.', title: 'Error' });
1191
1234
  return;
1192
1235
  }
1193
- const takenLesson = { lessonId: currentLesson.id, status: status, score: rates.score };
1236
+ const takenLesson = { id: currentLesson.id, goalCompleted: null, score: rates.score, status: status, lastAccess: new Date() };
1194
1237
  console.log('Lesson evaluation result:', takenLesson);
1195
1238
  // TODO: Re-implement saving the taken lesson status via lessonService
1196
1239
  // try {
@@ -1224,8 +1267,6 @@ class DCLessonRendererComponent {
1224
1267
  // Call the service to get the agent cards
1225
1268
  const conversationSettings = await this.lessonAIService.generateConversationSettingsForLesson(currentLesson);
1226
1269
  if (conversationSettings) {
1227
- // this.agentMasterLesson.set(agentCards);
1228
- // this.evaluatorAgentCard.set(agentCards.evaluatorAgent);
1229
1270
  this.conversationSettings.set(conversationSettings);
1230
1271
  this.chatVisible.set(true);
1231
1272
  console.log('Agent cards received and set.');
@@ -1240,12 +1281,24 @@ class DCLessonRendererComponent {
1240
1281
  this.toastrService.error({ subtitle: 'An error occurred while preparing the AI chat.', title: 'Error' });
1241
1282
  }
1242
1283
  }
1284
+ onVisibleChange(isVisible) {
1285
+ if (isVisible === false) {
1286
+ this.chatVisible.set(false);
1287
+ }
1288
+ }
1289
+ handleGoalCompleted(event) {
1290
+ console.log('Goal completed:', event);
1291
+ const lesson = this.lesson();
1292
+ const dummy = { id: lesson.id, goalCompleted: true, score: 100, status: 'finished', lastAccess: new Date() };
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!' });
1295
+ }
1243
1296
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1244
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonRendererComponent, isStandalone: true, selector: "dc-lesson-renderer", inputs: { lessonInput: { classPropertyName: "lessonInput", publicName: "lessonInput", isSignal: true, isRequired: false, transformFunction: null }, lessonIdInput: { classPropertyName: "lessonIdInput", publicName: "lessonIdInput", isSignal: true, isRequired: false, transformFunction: null }, test: { classPropertyName: "test", publicName: "test", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "dynamicLesson", first: true, predicate: ["dynamicLesson"], descendants: true, static: true }], ngImport: i0, template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [conversationSettings]=\"conversationSettings()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"], dependencies: [{ kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DCChatComponent, selector: "dc-chat", inputs: ["chatUserSettings", "conversationSettings", "agentCard", "evaluatorAgentCard", "parseDict"], outputs: ["sendMessage", "goalCompleted"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i2$4.Drawer, selector: "p-drawer", inputs: ["appendTo", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }] }); }
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"] }] }); }
1245
1298
  }
1246
1299
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonRendererComponent, decorators: [{
1247
1300
  type: Component,
1248
- args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [conversationSettings]=\"conversationSettings()\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"] }]
1301
+ args: [{ selector: 'dc-lesson-renderer', standalone: true, imports: [KeyValuePipe, ButtonModule, DCChatComponent, DrawerModule], template: "<div>\n <div #dynamicLesson class=\"targetclass\">\n <ng-template #target></ng-template>\n </div>\n</div>\n\n<br />\n<div style=\"display: flex; gap: 10px\">\n @if ((mainForm.controls | keyvalue)?.length) {\n <div>\n <p-button label=\"Calificar Lecci\u00F3n\" icon=\"pi pi-check-circle\" (click)=\"evaluateForms()\" [rounded]=\"true\"></p-button>\n </div>\n }\n\n <p-button icon=\"pi pi-verified\" [rounded]=\"true\" (click)=\"startAI()\" label=\"Repasar con IA\" />\n</div>\n<br /><br />\n\n@if(chatVisible()) {\n<p-drawer header=\"Conversation\" [visible]=\"chatVisible()\" (visibleChange)=\"onVisibleChange($event)\" position=\"bottom\" styleClass=\"app-bottom-overlay\">\n <dc-chat [appAgentTask]=\"evalAgentTask()\" [conversationSettings]=\"conversationSettings()\" (goalCompleted)=\"handleGoalCompleted($event)\"></dc-chat>\n</p-drawer>\n}\n", styles: [".evaluate{float:right}\n"] }]
1249
1302
  }], ctorParameters: () => [], propDecorators: { dynamicLesson: [{
1250
1303
  type: ViewChild,
1251
1304
  args: ['dynamicLesson', { static: true }]
@@ -1416,6 +1469,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
1416
1469
  type: Injectable
1417
1470
  }] });
1418
1471
 
1472
+ // import { UserDataExchangeService } from '@dataclouder/ngx-agent-cards';
1419
1473
  class LessonUtilsService {
1420
1474
  constructor() {
1421
1475
  this.#lessonService = inject(LESSONS_TOKEN);
@@ -1438,15 +1492,6 @@ class LessonUtilsService {
1438
1492
  }
1439
1493
  // Placeholder logic - adapt as needed from original component
1440
1494
  console.log('Validating audios for lesson:', lesson.id);
1441
- // Original logic commented out - requires SpeakerCompConfiguration type
1442
- // const needsGeneration = lesson.components.some((component: any) => // Use 'any' or define SpeakerCompConfiguration
1443
- // component.component === 'speaker' && component.settings?.voice && !component.audio
1444
- // );
1445
- // if (needsGeneration) {
1446
- // this.#toastService.warn({ title: 'Audios por generar', subtitle: 'Se encontraron audios pendientes' });
1447
- // // Consider calling generation service here or returning a flag
1448
- // // this.#lessonService.generateAudiosForLesson(lesson.id).then(res => { ... });
1449
- // }
1450
1495
  }
1451
1496
  /**
1452
1497
  * Updates the lesson signal with a new cover image.
@@ -1496,7 +1541,9 @@ class LessonUtilsService {
1496
1541
  });
1497
1542
  // Note: Saving the lesson should be triggered from the component after calling this.
1498
1543
  }
1544
+ // I want to deprecate this method
1499
1545
  /**
1546
+ * @deprecated Use create new method.
1500
1547
  * Generates lesson content using AI. Assumes the lesson is already saved.
1501
1548
  * @param lessonId The ID of the lesson to generate content for.
1502
1549
  * @returns The updated lesson object after AI generation, or null on failure.
@@ -1530,9 +1577,9 @@ class LessonUtilsService {
1530
1577
  /**
1531
1578
  * Improves the provided Markdown text using AI and updates the lesson.
1532
1579
  * Assumes the lesson is already saved before calling this.
1533
- * @param lessonId The ID of the lesson to update.
1580
+ * @param lesson The lesson object containing the prompt and other context.
1534
1581
  * @param markdownText The Markdown text generated from the lesson's HTML content.
1535
- * @returns The updated lesson object after AI improvement, or null on failure.
1582
+ * @returns The improved Markdown string, or null on failure.
1536
1583
  */
1537
1584
  async improveMDWithAI(lesson, markdownText) {
1538
1585
  if (!markdownText) {
@@ -1541,38 +1588,30 @@ class LessonUtilsService {
1541
1588
  }
1542
1589
  try {
1543
1590
  this.loadingBarService.showIndeterminate();
1544
- const agentCard = BasicAgentCard$1;
1545
- let task = `Improve the following lesson return only the lesson markdown: ${markdownText}`;
1546
- if (lesson.prompt) {
1547
- task = `${task} Also consider the original prompt: ${lesson.prompt} when improving the lesson.`;
1548
- }
1549
- const appTask = { task: task };
1550
- const messages = [
1551
- { content: agentCard.systemPrompt, role: ChatRole.System },
1552
- { content: appTask.task, role: ChatRole.User },
1553
- ];
1554
- const response = await this.#agentService.callChatCompletion({ messages, model: agentCard.model });
1555
- 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;
1556
1595
  // Remove potential markdown fences
1557
1596
  const markdownFenceStart = '```markdown\n';
1558
1597
  const markdownFenceEnd = '\n```';
1559
- if (improvedMarkdown.startsWith(markdownFenceStart) && improvedMarkdown.endsWith(markdownFenceEnd)) {
1598
+ if (improvedMarkdown?.startsWith(markdownFenceStart) && improvedMarkdown.endsWith(markdownFenceEnd)) {
1560
1599
  improvedMarkdown = improvedMarkdown.slice(markdownFenceStart.length, -markdownFenceEnd.length);
1561
1600
  }
1562
1601
  else {
1563
1602
  // Also handle cases where it might just be ``` at the start/end
1564
1603
  const simpleFence = '```';
1565
- if (improvedMarkdown.startsWith(simpleFence)) {
1604
+ if (improvedMarkdown?.startsWith(simpleFence)) {
1566
1605
  improvedMarkdown = improvedMarkdown.slice(simpleFence.length);
1567
1606
  }
1568
- if (improvedMarkdown.endsWith(simpleFence)) {
1607
+ if (improvedMarkdown?.endsWith(simpleFence)) {
1569
1608
  improvedMarkdown = improvedMarkdown.slice(0, -simpleFence.length);
1570
1609
  }
1571
1610
  }
1572
1611
  // Trim any leading/trailing whitespace that might remain after removing fences
1573
- improvedMarkdown = improvedMarkdown.trim();
1612
+ improvedMarkdown = improvedMarkdown?.trim() ?? null;
1574
1613
  console.log('Improved Markdown:', improvedMarkdown);
1575
- return improvedMarkdown;
1614
+ return improvedMarkdown; // Return only the string
1576
1615
  }
1577
1616
  catch (error) {
1578
1617
  console.error('Error during AI Markdown improvement in service:', error);
@@ -1582,7 +1621,78 @@ class LessonUtilsService {
1582
1621
  finally {
1583
1622
  this.loadingBarService.successAndHide();
1584
1623
  }
1585
- // Loading state should be managed by the component calling this service.
1624
+ }
1625
+ /**
1626
+ * Generates a concise description for the lesson using AI based on its content.
1627
+ * @param lesson The lesson object containing the content.
1628
+ * @returns A promise resolving to the generated description string, or null on failure.
1629
+ */
1630
+ async generateDescriptionWithAI(lesson) {
1631
+ if (!lesson || !lesson.textCoded) {
1632
+ this.#toastService.warn({ title: 'Contenido Requerido', subtitle: 'Se necesita contenido en la lección para generar una descripción.' });
1633
+ return null;
1634
+ }
1635
+ try {
1636
+ this.loadingBarService.showIndeterminate(); // Corrected: No argument needed
1637
+ // Extract plain text from the lesson's HTML content
1638
+ const plainTextContent = this._extractTextFromHtml(lesson.textCoded);
1639
+ if (!plainTextContent || plainTextContent.trim().length === 0) {
1640
+ this.#toastService.warn({ title: 'Texto Vacío', subtitle: 'No se pudo extraer texto útil del contenido de la lección.' });
1641
+ return null;
1642
+ }
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' } });
1646
+ let generatedDescription = response.content?.trim() ?? null;
1647
+ // Basic cleanup (remove potential quotes if the AI wraps the response)
1648
+ if (generatedDescription && generatedDescription.startsWith('"') && generatedDescription.endsWith('"')) {
1649
+ generatedDescription = generatedDescription.substring(1, generatedDescription.length - 1);
1650
+ }
1651
+ if (generatedDescription) {
1652
+ this.#toastService.success({ title: 'Descripción Generada', subtitle: 'Se generó la descripción con IA.' });
1653
+ return generatedDescription;
1654
+ }
1655
+ else {
1656
+ throw new Error('AI did not return a valid description.');
1657
+ }
1658
+ }
1659
+ catch (error) {
1660
+ console.error('Error during AI description generation in service:', error);
1661
+ this.#toastService.error({ title: 'Error de IA', subtitle: 'No se pudo generar la descripción con IA.' });
1662
+ return null;
1663
+ }
1664
+ finally {
1665
+ this.loadingBarService.successAndHide();
1666
+ }
1667
+ }
1668
+ /**
1669
+ * Extracts plain text from an HTML string, removing encoded JSON components.
1670
+ * @param htmlInput The HTML string potentially containing encoded JSON (~...~).
1671
+ * @returns The plain text representation.
1672
+ */
1673
+ _extractTextFromHtml(htmlInput) {
1674
+ if (!htmlInput)
1675
+ return '';
1676
+ // 1. Replace encoded JSON blocks with their 'settings.text' or an empty string
1677
+ const jsonRegex = /~(.+?)~/g;
1678
+ const textWithPlaceholders = htmlInput.replace(jsonRegex, (match, jsonCoded) => {
1679
+ try {
1680
+ const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
1681
+ const data = JSON.parse(decodedJson);
1682
+ return data?.settings?.text || ''; // Return text or empty string if not found
1683
+ }
1684
+ catch (error) {
1685
+ console.error('Failed to parse JSON inside HTML during text extraction:', jsonCoded, error);
1686
+ return ''; // Return empty string on error
1687
+ }
1688
+ });
1689
+ // 2. Strip remaining HTML tags to get plain text
1690
+ // Create a temporary DOM element to parse the HTML
1691
+ const tempDiv = document.createElement('div');
1692
+ tempDiv.innerHTML = textWithPlaceholders;
1693
+ // Use textContent to get the plain text, effectively stripping tags
1694
+ // Replace multiple whitespace characters (including newlines) with a single space
1695
+ return (tempDiv.textContent || tempDiv.innerText || '').replace(/\s+/g, ' ').trim();
1586
1696
  }
1587
1697
  /**
1588
1698
  * Cleans orphaned components from the lesson's dynamicComponents.
@@ -1623,7 +1733,6 @@ class LessonUtilsService {
1623
1733
  orphanedIds.push(componentId);
1624
1734
  }
1625
1735
  }
1626
- // Log a warning if any orphaned components were found
1627
1736
  if (orphanedIds.length > 0) {
1628
1737
  console.warn(`[LessonUtilsService] Orphaned components detected and will be removed from lesson data (IDs not found in textCoded):`, orphanedIds);
1629
1738
  this.#toastService.warn({
@@ -1691,22 +1800,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
1691
1800
  type: Output
1692
1801
  }] } });
1693
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
+
1694
1838
  class DCLessonMetadataEditorComponent {
1695
1839
  constructor() {
1696
1840
  // Use signal for input lesson data
1697
- this.lesson = signal(undefined);
1698
- this.isLoadingLesson = signal(false);
1841
+ this.lesson = signal(undefined); // The lesson data itself
1842
+ this.isLoadingLesson = signal(false); // Shared loading state
1699
1843
  // Outputs for actions
1700
1844
  this.saveRequest = new EventEmitter();
1701
1845
  this.importNotionRequest = new EventEmitter();
1702
1846
  this.improveNotionRequest = new EventEmitter();
1703
- this.generateAIRequest = new EventEmitter();
1704
- // Output for property changes
1705
- this.propertyChange = new EventEmitter();
1847
+ // Removed generateAIRequest Output as it's handled internally now
1848
+ // Output removed as the component now updates the input signal directly.
1849
+ // @Output() propertyChange = new EventEmitter<{ propertyPath: string; value: any }>();
1850
+ // Injected Services
1851
+ this.#lessonUtilsService = inject(LessonUtilsService);
1852
+ this.#toastService = inject(TOAST_ALERTS_TOKEN);
1853
+ this.#lessonService = inject(LESSONS_TOKEN);
1854
+ this.#turndownService = new TurndownService(); // Instantiate TurndownService
1706
1855
  }
1707
- // Method to emit property changes, called by ngModelChange in the template
1708
- onPropertyChange(property, value) {
1709
- this.propertyChange.emit({ property, value });
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 }>();
1859
+ // Injected Services
1860
+ #lessonUtilsService;
1861
+ #toastService;
1862
+ #lessonService;
1863
+ #turndownService; // Instantiate TurndownService
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 });
1710
1892
  }
1711
1893
  // Methods to emit action requests
1712
1894
  emitSaveRequest() {
@@ -1718,13 +1900,110 @@ class DCLessonMetadataEditorComponent {
1718
1900
  emitImproveNotionRequest() {
1719
1901
  this.improveNotionRequest.emit();
1720
1902
  }
1721
- emitGenerateAIRequest() {
1722
- this.generateAIRequest.emit();
1903
+ /**
1904
+ * Generates lesson content using AI, saving the current state first.
1905
+ * Moved from DCLessonEditorComponent.
1906
+ */
1907
+ async generateByAI() {
1908
+ const currentLesson = this.lesson(); // Get current value
1909
+ if (!currentLesson?.id) {
1910
+ this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
1911
+ return;
1912
+ }
1913
+ this.isLoadingLesson.set(true);
1914
+ try {
1915
+ const rawHtmlContent = currentLesson.textCoded || '';
1916
+ if (!rawHtmlContent) {
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);
1924
+ }
1925
+ else {
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
+ }
1953
+ }
1954
+ }
1955
+ catch (error) {
1956
+ console.error('Error during AI generation process in metadata editor:', error);
1957
+ // Service handles specific AI error toasts, maybe add a general one here if needed
1958
+ // this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
1959
+ }
1960
+ finally {
1961
+ this.isLoadingLesson.set(false); // Stop loading
1962
+ }
1963
+ }
1964
+ /**
1965
+ * Replaces encoded JSON blocks (~...~) in HTML with their 'settings.text' value.
1966
+ * Moved from DCLessonEditorComponent.
1967
+ * @param htmlInput The HTML string containing encoded JSON.
1968
+ * @returns The processed HTML string with text replacements.
1969
+ */
1970
+ _extractTextFromEncodedJson(htmlInput) {
1971
+ const jsonRegex = /~(.+?)~/g; // Global flag to replace all occurrences
1972
+ return htmlInput.replace(jsonRegex, (match, jsonCoded) => {
1973
+ try {
1974
+ // Attempt to decode HTML entities that might be present in the JSON string
1975
+ const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
1976
+ const data = JSON.parse(decodedJson);
1977
+ // Return the text if available, otherwise keep the original match (or an error placeholder)
1978
+ return data?.settings?.text || match;
1979
+ }
1980
+ catch (error) {
1981
+ console.error('Failed to parse JSON inside HTML:', jsonCoded, error);
1982
+ return '<!-- invalid component data -->'; // Placeholder for parsing errors
1983
+ }
1984
+ });
1985
+ }
1986
+ /**
1987
+ * Calls the LessonUtilsService to generate a description using AI
1988
+ * and updates the lesson signal if successful.
1989
+ */
1990
+ async triggerGenerateDescriptionAI() {
1991
+ const currentLesson = this.lesson();
1992
+ if (!currentLesson) {
1993
+ this.#toastService.warn({ title: 'Lección no cargada', subtitle: 'Espera a que la lección se cargue.' });
1994
+ return;
1995
+ }
1996
+ const generatedDescription = await this.#lessonUtilsService.generateDescriptionWithAI(currentLesson);
1997
+ if (generatedDescription) {
1998
+ // Use the new method for metadata properties
1999
+ this.onMetadataPropertyChange('description', generatedDescription);
2000
+ }
1723
2001
  }
1724
2002
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1725
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonMetadataEditorComponent, isStandalone: true, selector: "dc-lesson-metadata-editor", inputs: { lesson: "lesson", isLoadingLesson: "isLoadingLesson" }, outputs: { saveRequest: "saveRequest", importNotionRequest: "importNotionRequest", improveNotionRequest: "improveNotionRequest", generateAIRequest: "generateAIRequest", 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 <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 </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)=\"emitGenerateAIRequest()\" />\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: "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
1726
2004
  FlagLanguagePipe, name: "flagEmoji" }, { kind: "pipe", type: // Added Pipe
1727
- LangDescTranslationPipe, name: "langDesc" }] }); }
2005
+ LangDescTranslationPipe, name: "langDesc" }, { kind: "ngmodule", type: // Added Pipe
2006
+ InputGroupModule }, { kind: "component", type: i5$1.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["style", "styleClass"] }] }); }
1728
2007
  }
1729
2008
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonMetadataEditorComponent, decorators: [{
1730
2009
  type: Component,
@@ -1736,7 +2015,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
1736
2015
  TooltipModule, // Added TooltipModule
1737
2016
  FlagLanguagePipe, // Added Pipe
1738
2017
  LangDescTranslationPipe, // Added Pipe
1739
- ], 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 <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 </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)=\"emitGenerateAIRequest()\" />\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" }]
2018
+ InputGroupModule,
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" }]
1740
2020
  }], propDecorators: { lesson: [{
1741
2021
  type: Input,
1742
2022
  args: [{ required: true }]
@@ -1749,12 +2029,112 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
1749
2029
  type: Output
1750
2030
  }], improveNotionRequest: [{
1751
2031
  type: Output
1752
- }], generateAIRequest: [{
1753
- type: Output
1754
- }], propertyChange: [{
1755
- type: Output
1756
2032
  }] } });
1757
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
+
1758
2138
  class DCLessonEditorComponent {
1759
2139
  // Services
1760
2140
  #activatedRoute; // Re-inject as it's needed for navigation
@@ -1763,6 +2143,7 @@ class DCLessonEditorComponent {
1763
2143
  #router;
1764
2144
  #lessonService;
1765
2145
  #toastService;
2146
+ #loadingBarService;
1766
2147
  constructor() {
1767
2148
  // Services
1768
2149
  this.#activatedRoute = inject(ActivatedRoute); // Re-inject as it's needed for navigation
@@ -1771,15 +2152,31 @@ class DCLessonEditorComponent {
1771
2152
  this.#router = inject(Router);
1772
2153
  this.#lessonService = inject(LESSONS_TOKEN);
1773
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);
1774
2161
  // Signals States
1775
2162
  this.lessonId = toSignal(inject(ActivatedRoute).paramMap.pipe(map((params) => params.get('id'))));
1776
2163
  this.lesson = signal(undefined); // Initialize as undefined
1777
2164
  this.isLoadingLesson = signal(false);
1778
2165
  // Computed Signals
1779
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
1780
2168
  const currentLesson = this.lesson();
1781
- const coverImage = currentLesson?.media?.images?.find((img) => img.type === 'cover');
1782
- 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';
1783
2180
  });
1784
2181
  // Computed signal to get dynamic components as an array for easier iteration in the template
1785
2182
  this.dynamicComponentsArray = computed(() => {
@@ -1798,6 +2195,7 @@ class DCLessonEditorComponent {
1798
2195
  fileName: 'cover',
1799
2196
  cropSettings: { resizeToWidth: 850, aspectRatio: AspectType.RectangleLarge, resolutions: [ResolutionType.Medium] },
1800
2197
  };
2198
+ this.promptsVisible = false;
1801
2199
  // Effect to fetch lesson data when ID changes
1802
2200
  effect(async () => {
1803
2201
  const id = this.lessonId();
@@ -1921,15 +2319,6 @@ class DCLessonEditorComponent {
1921
2319
  const newComponent = result?.obj;
1922
2320
  if (newComponent?.id) {
1923
2321
  console.log('Component builder closed, result received in editor:', newComponent);
1924
- // // Transform LessonComponentConfiguration to DynamicContentComponent
1925
- // const dynamicComponentToAdd: DynamicContentComponent = {
1926
- // id: componentConfig.id,
1927
- // component: componentConfig.component,
1928
- // inputs: {
1929
- // config: componentConfig, // Pass the original config object as an input named 'config'
1930
- // // Add other potential inputs if needed based on component type later
1931
- // },
1932
- // };
1933
2322
  // Update the lesson signal, adding the transformed component to the dynamicComponents object
1934
2323
  this.lesson.update((currentLesson) => {
1935
2324
  if (!currentLesson)
@@ -1949,75 +2338,8 @@ class DCLessonEditorComponent {
1949
2338
  }
1950
2339
  }
1951
2340
  // isLoadingLesson signal is used directly
1952
- async generateByAI() {
1953
- const currentId = this.lessonId();
1954
- if (!currentId) {
1955
- this.#toastService.warn({ title: 'Guardar primero', subtitle: 'Guarda la lección antes de usar IA.' });
1956
- return;
1957
- }
1958
- this.isLoadingLesson.set(true);
1959
- try {
1960
- // Ensure latest changes are saved before generating
1961
- const savedLesson = await this.saveLesson();
1962
- if (!savedLesson) {
1963
- // Handle save error - toast is shown in saveLesson
1964
- throw new Error('Failed to save before AI generation');
1965
- }
1966
- // // Removed debugger
1967
- const rawHtmlContent = this.lesson()?.textCoded || '';
1968
- if (!rawHtmlContent) {
1969
- console.warn('No HTML content found in lesson to process.');
1970
- this.isLoadingLesson.set(false);
1971
- return; // Exit if no content
1972
- }
1973
- // Replace encoded JSON with actual text before Markdown conversion
1974
- const processedHtmlContent = this._extractTextFromEncodedJson(rawHtmlContent);
1975
- const turndownService = new TurndownService();
1976
- // Convert the processed HTML (with text instead of JSON) to Markdown
1977
- const markdownText = turndownService.turndown(processedHtmlContent);
1978
- const improvedMarkdown = await this.#lessonUtilsService.improveMDWithAI(this.lesson(), markdownText);
1979
- if (improvedMarkdown) {
1980
- // Convert the improved Markdown back to HTML before setting it
1981
- const improvedHtml = marked(improvedMarkdown);
1982
- this.lesson.set({ ...this.lesson(), textCoded: improvedHtml }); // Update the signal with the converted HTML
1983
- // Toast success is handled by the service
1984
- }
1985
- else {
1986
- // Toast error is handled by the service
1987
- throw new Error('AI generation failed or lesson fetch failed after generation.');
1988
- }
1989
- }
1990
- catch (error) {
1991
- // Type error
1992
- console.error('Error during AI generation process in component:', error);
1993
- // Service handles specific AI error toasts, maybe add a general one here if needed
1994
- // this.#toastService.error({ title: 'Error General', subtitle: 'Ocurrió un problema durante el proceso de IA.' });
1995
- }
1996
- finally {
1997
- this.isLoadingLesson.set(false); // Stop loading
1998
- }
1999
- }
2000
- /**
2001
- * Replaces encoded JSON blocks (~...~) in HTML with their 'settings.text' value.
2002
- * @param htmlInput The HTML string containing encoded JSON.
2003
- * @returns The processed HTML string with text replacements.
2004
- */
2005
- _extractTextFromEncodedJson(htmlInput) {
2006
- const jsonRegex = /~(.+?)~/g; // Global flag to replace all occurrences
2007
- return htmlInput.replace(jsonRegex, (match, jsonCoded) => {
2008
- try {
2009
- // Attempt to decode HTML entities that might be present in the JSON string
2010
- const decodedJson = jsonCoded.replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
2011
- const data = JSON.parse(decodedJson);
2012
- // Return the text if available, otherwise keep the original match (or an error placeholder)
2013
- return data?.settings?.text || match;
2014
- }
2015
- catch (error) {
2016
- console.error('Failed to parse JSON inside HTML:', jsonCoded, error);
2017
- return '<!-- invalid component data -->'; // Placeholder for parsing errors
2018
- }
2019
- });
2020
- }
2341
+ // Removed generateByAI and _extractTextFromEncodedJson methods.
2342
+ // This logic is now handled within DCLessonMetadataEditorComponent.
2021
2343
  /**
2022
2344
  * Handles the image upload event, updates the lesson signal via the service, and saves.
2023
2345
  * @param event The image upload event data.
@@ -2059,9 +2381,51 @@ class DCLessonEditorComponent {
2059
2381
  showComponentDetails(data) {
2060
2382
  alert('showComponentDetails' + JSON.stringify(data));
2061
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
+ }
2062
2425
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2063
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCLessonEditorComponent, isStandalone: true, selector: "dc-lesson-editor", providers: [LessonNotionService], viewQueries: [{ propertyName: "target", first: true, predicate: ["target"], descendants: true, read: ViewContainerRef }, { propertyName: "dhtml", first: true, predicate: ["dhtml"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (generateAIRequest)=\"generateByAI()\"\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$1.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
2064
- DCLessonMetadataEditorComponent, selector: "dc-lesson-metadata-editor", inputs: ["lesson", "isLoadingLesson"], outputs: ["saveRequest", "importNotionRequest", "improveNotionRequest", "generateAIRequest", "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"] }] }); }
2065
2429
  }
2066
2430
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCLessonEditorComponent, decorators: [{
2067
2431
  type: Component,
@@ -2076,7 +2440,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
2076
2440
  TooltipModule,
2077
2441
  DCLessonComponentAdderComponent, // Add the component adder here
2078
2442
  DCLessonMetadataEditorComponent, // Add the metadata editor here
2079
- ], providers: [LessonNotionService], template: "<div style=\"position: relative; margin-bottom: 20px\">\n <img class=\"header-cover\" [src]=\"coverImageUrl()\" alt=\"Lesson Cover Image\" />\n\n <dc-cropper-modal\n style=\"position: absolute; top: 10px; left: 20px\"\n [buttonLabel]=\"'Carga una portada'\"\n [imgStorageSettings]=\"coverStorageSettings\"\n (imageUploaded)=\"onImageUploaded($event)\"></dc-cropper-modal>\n</div>\n\n<br />\n\n<!-- Lesson Metadata Editor -->\n<dc-lesson-metadata-editor\n [lesson]=\"lesson\"\n [isLoadingLesson]=\"isLoadingLesson\"\n (saveRequest)=\"saveLesson()\"\n (importNotionRequest)=\"importFromNotion()\"\n (improveNotionRequest)=\"improveNotionWithAI()\"\n (generateAIRequest)=\"generateByAI()\"\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"] }]
2080
2445
  }], ctorParameters: () => [], propDecorators: { target: [{
2081
2446
  type: ViewChild,
2082
2447
  args: ['target', { read: ViewContainerRef }]
@@ -2112,5 +2477,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
2112
2477
  * Generated bundle index. Do not edit.
2113
2478
  */
2114
2479
 
2115
- export { BasicAgentCard$1 as BasicAgentCard, ComponentBuilder, ComponentWithForm, DCLessonEditorComponent, DCLessonFormComponent, DCLessonListComponent, DCLessonRendererComponent, DcLessonCardComponent, FlagLanguagePipe, LESSONS_TOKEN, LangCodeDescription, LangCodeDescriptionEs, LangDescTranslationPipe, LessonComponentBuilders, LessonComponentEnum, LessonComponents, LessonDynamicComponent, LessonsAbstractService, NOTION_SERVICE_TOKEN, NotionAbstractService, NotionExportType, SelectorBuilderComponent, SelectorComponent, TextWriterBuiderComponent, TextWriterComponent, TranslationSwitcherBuilderComponent, TranslationSwitcherComponent, getLessonComponentClass, provideLessonsService, provideNotionService };
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 };
2116
2481
  //# sourceMappingURL=dataclouder-ngx-lessons.mjs.map