valtech-components 2.0.1003 → 2.0.1005

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.
@@ -25,7 +25,7 @@ import { getApps, getApp, initializeApp as initializeApp$1 } from 'firebase/app'
25
25
  import * as i2$1 from '@ionic/angular/standalone';
26
26
  import { AlertController, IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonNote, IonDatetimeButton, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
27
27
  import { addIcons } from 'ionicons';
28
- import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, menuOutline } from 'ionicons/icons';
28
+ import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, helpCircleOutline, menuOutline } from 'ionicons/icons';
29
29
  import { Browser } from '@capacitor/browser';
30
30
  import * as i1$6 from '@angular/platform-browser';
31
31
  import { DomSanitizer, Meta, Title } from '@angular/platform-browser';
@@ -54,7 +54,7 @@ import 'prismjs/components/prism-json';
54
54
  * Current version of valtech-components.
55
55
  * This is automatically updated during the publish process.
56
56
  */
57
- const VERSION = '2.0.1003';
57
+ const VERSION = '2.0.1005';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -21657,7 +21657,7 @@ class CommentComponent {
21657
21657
  </div>
21658
21658
  }
21659
21659
  </div>
21660
- `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}:host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(.6875rem,.8125rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(.6875rem,.75rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(.6875rem,.6875rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(.6875rem,.625rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(.6875rem,.5625rem)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"], dependencies: [{ kind: "component", type: CommentComponent, selector: "val-comment", inputs: ["props"], outputs: ["authorClick", "reactionClick", "actionClick", "menuItemClick", "loadMoreClick", "collapseToggle"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonPopover, selector: "ion-popover" }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: AvatarComponent, selector: "val-avatar", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
21660
+ `, isInline: true, styles: [":host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(11px,14px - 1px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(11px,14px - 2px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(11px,14px - 3px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(11px,14px - 4px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(11px,14px - 5px)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"], dependencies: [{ kind: "component", type: CommentComponent, selector: "val-comment", inputs: ["props"], outputs: ["authorClick", "reactionClick", "actionClick", "menuItemClick", "loadMoreClick", "collapseToggle"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonPopover, selector: "ion-popover" }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: AvatarComponent, selector: "val-avatar", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
21661
21661
  }
21662
21662
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CommentComponent, decorators: [{
21663
21663
  type: Component,
@@ -21847,7 +21847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
21847
21847
  </div>
21848
21848
  }
21849
21849
  </div>
21850
- `, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}:host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(.6875rem,.8125rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(.6875rem,.75rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(.6875rem,.6875rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(.6875rem,.625rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(.6875rem,.5625rem)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"] }]
21850
+ `, styles: [":host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(11px,14px - 1px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(11px,14px - 2px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(11px,14px - 3px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(11px,14px - 4px)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(11px,14px - 5px)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"] }]
21851
21851
  }], propDecorators: { props: [{
21852
21852
  type: Input
21853
21853
  }], authorClick: [{
@@ -41737,7 +41737,7 @@ const PROFILE_VIEW_I18N = {
41737
41737
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41738
41738
  const HANDLE_MIN = 3;
41739
41739
  const HANDLE_MAX = 20;
41740
- const DEFAULT_NAMESPACE$f = 'Settings.Profile';
41740
+ const DEFAULT_NAMESPACE$g = 'Settings.Profile';
41741
41741
  /**
41742
41742
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41743
41743
  *
@@ -41787,7 +41787,7 @@ class ProfileViewComponent {
41787
41787
  showPhone: merged.showPhone ?? true,
41788
41788
  showShareCta: merged.showShareCta ?? true,
41789
41789
  profileBaseUrl: merged.profileBaseUrl ?? '',
41790
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
41790
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$g,
41791
41791
  onSaved: merged.onSaved,
41792
41792
  onAvatarUploaded: merged.onAvatarUploaded,
41793
41793
  };
@@ -42263,12 +42263,17 @@ class ProfileViewComponent {
42263
42263
 
42264
42264
  <val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
42265
42265
 
42266
- <!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
42267
- Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
42266
+ @if (resolvedConfig().showShareCta && p.handle) {
42267
+ <section class="settings-section share-cta-section">
42268
+ <val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
42269
+ </section>
42270
+ }
42268
42271
  }
42269
42272
  }
42270
42273
  </div>
42271
- `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: AvatarUploadComponent, selector: "val-avatar-upload", inputs: ["props"], outputs: ["uploaded", "error", "uploadStart"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }] }); }
42274
+
42275
+ <val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
42276
+ `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: AvatarUploadComponent, selector: "val-avatar-upload", inputs: ["props"], outputs: ["uploaded", "error", "uploadStart"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: ShareProfileModalComponent, selector: "val-share-profile-modal", inputs: ["isOpen", "props"], outputs: ["dismissed", "shared"] }] }); }
42272
42277
  }
42273
42278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ProfileViewComponent, decorators: [{
42274
42279
  type: Component,
@@ -42344,11 +42349,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
42344
42349
 
42345
42350
  <val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
42346
42351
 
42347
- <!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
42348
- Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
42352
+ @if (resolvedConfig().showShareCta && p.handle) {
42353
+ <section class="settings-section share-cta-section">
42354
+ <val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
42355
+ </section>
42356
+ }
42349
42357
  }
42350
42358
  }
42351
42359
  </div>
42360
+
42361
+ <val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
42352
42362
  `, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"] }]
42353
42363
  }], ctorParameters: () => [], propDecorators: { config: [{
42354
42364
  type: Input
@@ -42479,15 +42489,6 @@ class PreferencesService {
42479
42489
  this._updateInFlight = false;
42480
42490
  const destroyRef = inject(DestroyRef);
42481
42491
  destroyRef.onDestroy(() => this.unbind());
42482
- // Seed `_fontSize` con el valor que FontSizeService ya restauró desde
42483
- // localStorage en su constructor (providedIn root → ya corrió al inyectarlo
42484
- // acá). Sin esto, el signal arranca en 'medium' y el effect de abajo —que
42485
- // dispara al marcarse `synced`— forzaría 'medium' pisando el tamaño
42486
- // restaurado cuando el snapshot Firestore NO trae `fontSize` (user sin ese
42487
- // campo en el mirror). Bug: cambias el tamaño, refrescas y se pierde.
42488
- if (this.fontSizeService) {
42489
- this._fontSize.set(this.fontSizeService.size());
42490
- }
42491
42492
  // Auto-bind al user actual. effect() corre en injection context (constructor OK).
42492
42493
  // allowSignalWrites: bindToUser/unbind escriben `_synced` (señal interna sin
42493
42494
  // dependencias del propio effect) → seguro y necesario.
@@ -42522,17 +42523,11 @@ class PreferencesService {
42522
42523
  this.i18n.setLanguage(l);
42523
42524
  }
42524
42525
  });
42525
- // Side-effect: aplicar fontSize.
42526
- // SIN gate `_synced` (a diferencia de theme/language): el tamaño es una
42527
- // preferencia local-first — FontSizeService/localStorage es la verdad del
42528
- // dispositivo y `_fontSize` ya viene seedeado de ahí arriba. Aplicar siempre
42529
- // que cambie permite que el cambio del usuario surta efecto al instante
42530
- // (optimista, sin esperar el round-trip a Firestore) y que el sync
42531
- // cross-device también aplique al llegar el snapshot. Gatearlo en `_synced`
42532
- // hacía que el primer cambio de un user nuevo (sin doc Firestore aún) no se
42533
- // aplicara: el selector mostraba "Grande" pero el font-size no cambiaba.
42526
+ // Side-effect: aplicar fontSize local cuando llega snapshot real.
42534
42527
  // allowSignalWrites: fontSizeService.setSize() escribe this._size (signal).
42535
42528
  effect(() => {
42529
+ if (!this._synced())
42530
+ return;
42536
42531
  const fs = this._fontSize();
42537
42532
  if (this.fontSizeService) {
42538
42533
  this.fontSizeService.setSize(fs);
@@ -42708,7 +42703,7 @@ const PREFERENCES_VIEW_I18N = {
42708
42703
  },
42709
42704
  };
42710
42705
 
42711
- const DEFAULT_NAMESPACE$e = 'Settings.General';
42706
+ const DEFAULT_NAMESPACE$f = 'Settings.General';
42712
42707
  const DEFAULT_LANGUAGES = ['es', 'en'];
42713
42708
  /**
42714
42709
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42745,7 +42740,7 @@ class PreferencesViewComponent {
42745
42740
  showLanguage: merged.showLanguage ?? true,
42746
42741
  showFontSize: merged.showFontSize ?? true,
42747
42742
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42748
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
42743
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
42749
42744
  onThemeChanged: merged.onThemeChanged,
42750
42745
  onLanguageChanged: merged.onLanguageChanged,
42751
42746
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44501,7 +44496,7 @@ const SECURITY_VIEW_I18N = {
44501
44496
  };
44502
44497
 
44503
44498
  addIcons({ laptopOutline, phonePortraitOutline });
44504
- const DEFAULT_NAMESPACE$d = 'Settings.Security';
44499
+ const DEFAULT_NAMESPACE$e = 'Settings.Security';
44505
44500
  /**
44506
44501
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44507
44502
  *
@@ -44550,7 +44545,7 @@ class SecurityViewComponent {
44550
44545
  showChangeEmail: merged.showChangeEmail ?? true,
44551
44546
  showMfa: merged.showMfa ?? true,
44552
44547
  showPolicies: merged.showPolicies ?? true,
44553
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$d,
44548
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
44554
44549
  handleDeepLinks: merged.handleDeepLinks ?? true,
44555
44550
  homeRoute: merged.homeRoute ?? '/app/home',
44556
44551
  onMfaCompleted: merged.onMfaCompleted,
@@ -45456,7 +45451,7 @@ const CREATE_ORG_MODAL_I18N = {
45456
45451
  },
45457
45452
  };
45458
45453
 
45459
- const DEFAULT_NAMESPACE$c = 'CreateOrgModal';
45454
+ const DEFAULT_NAMESPACE$d = 'CreateOrgModal';
45460
45455
  /**
45461
45456
  * `val-create-org-modal` — modal de creación de organización (organism
45462
45457
  * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
@@ -45482,10 +45477,10 @@ class CreateOrgModalComponent {
45482
45477
  this.toast = inject(ToastService);
45483
45478
  this.errors = inject(ValtechErrorService);
45484
45479
  /** Namespace i18n con que la vista resuelve sus textos. */
45485
- this.i18nNamespace = DEFAULT_NAMESPACE$c;
45480
+ this.i18nNamespace = DEFAULT_NAMESPACE$d;
45486
45481
  this._busy = signal(false);
45487
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45488
- this.i18n.registerContent(DEFAULT_NAMESPACE$c, CREATE_ORG_MODAL_I18N);
45482
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$d)) {
45483
+ this.i18n.registerContent(DEFAULT_NAMESPACE$d, CREATE_ORG_MODAL_I18N);
45489
45484
  }
45490
45485
  this.formMeta = signal(this.buildFormMeta());
45491
45486
  effect(() => {
@@ -45721,7 +45716,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
45721
45716
  },
45722
45717
  };
45723
45718
 
45724
- const DEFAULT_NAMESPACE$b = 'Settings.DeleteAccount';
45719
+ const DEFAULT_NAMESPACE$c = 'Settings.DeleteAccount';
45725
45720
  /**
45726
45721
  * `val-delete-account-modal` — modal de eliminación de cuenta (organism,
45727
45722
  * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -45747,7 +45742,7 @@ class DeleteAccountModalComponent {
45747
45742
  this.i18n = inject(I18nService);
45748
45743
  this.toast = inject(ToastService);
45749
45744
  /** Namespace i18n con que la vista resuelve sus textos. */
45750
- this.i18nNamespace = DEFAULT_NAMESPACE$b;
45745
+ this.i18nNamespace = DEFAULT_NAMESPACE$c;
45751
45746
  this.hasPassword = signal(null);
45752
45747
  this.codeSent = signal(false);
45753
45748
  this.formState = signal(ComponentStates.ENABLED);
@@ -45814,8 +45809,8 @@ class DeleteAccountModalComponent {
45814
45809
  ],
45815
45810
  actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45816
45811
  }));
45817
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
45818
- this.i18n.registerContent(DEFAULT_NAMESPACE$b, DELETE_ACCOUNT_MODAL_I18N);
45812
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45813
+ this.i18n.registerContent(DEFAULT_NAMESPACE$c, DELETE_ACCOUNT_MODAL_I18N);
45819
45814
  }
45820
45815
  }
45821
45816
  ngOnInit() {
@@ -46016,7 +46011,7 @@ const ORG_INFO_SHEET_I18N = {
46016
46011
  },
46017
46012
  };
46018
46013
 
46019
- const DEFAULT_NAMESPACE$a = 'OrgInfoSheet';
46014
+ const DEFAULT_NAMESPACE$b = 'OrgInfoSheet';
46020
46015
  /**
46021
46016
  * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46022
46017
  * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
@@ -46061,15 +46056,15 @@ class OrgInfoSheetComponent {
46061
46056
  text: this.closeProps.text || this.resolvedCloseLabel(),
46062
46057
  }));
46063
46058
  addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46064
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46065
- this.i18n.registerContent(DEFAULT_NAMESPACE$a, ORG_INFO_SHEET_I18N);
46059
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
46060
+ this.i18n.registerContent(DEFAULT_NAMESPACE$b, ORG_INFO_SHEET_I18N);
46066
46061
  }
46067
46062
  }
46068
46063
  dismiss() {
46069
46064
  this._modalRef?.dismiss(null, 'cancel');
46070
46065
  }
46071
46066
  tt(key) {
46072
- return this.i18n.t(key, DEFAULT_NAMESPACE$a);
46067
+ return this.i18n.t(key, DEFAULT_NAMESPACE$b);
46073
46068
  }
46074
46069
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46075
46070
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrgInfoSheetComponent, isStandalone: true, selector: "val-org-info-sheet", inputs: { title: "title", subtitle: "subtitle", body: "body", closeLabel: "closeLabel", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
@@ -46236,7 +46231,7 @@ const SWITCH_ORG_MODAL_I18N = {
46236
46231
  },
46237
46232
  };
46238
46233
 
46239
- const DEFAULT_NAMESPACE$9 = 'Settings.SwitchOrg';
46234
+ const DEFAULT_NAMESPACE$a = 'Settings.SwitchOrg';
46240
46235
  addIcons({ businessOutline, checkmarkCircleOutline });
46241
46236
  /**
46242
46237
  * `val-switch-org-modal` — modal de cambio de organización activa (organism
@@ -46262,7 +46257,7 @@ class SwitchOrgModalComponent {
46262
46257
  this.orgSwitch = inject(OrgSwitchService);
46263
46258
  this.errors = inject(ValtechErrorService);
46264
46259
  /** Namespace i18n con que la vista resuelve sus textos. */
46265
- this.i18nNamespace = DEFAULT_NAMESPACE$9;
46260
+ this.i18nNamespace = DEFAULT_NAMESPACE$a;
46266
46261
  this.orgs = signal([]);
46267
46262
  this.loading = signal(false);
46268
46263
  this.query = signal('');
@@ -46278,8 +46273,8 @@ class SwitchOrgModalComponent {
46278
46273
  return list;
46279
46274
  return list.filter(o => o.name.toLowerCase().includes(q));
46280
46275
  });
46281
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
46282
- this.i18n.registerContent(DEFAULT_NAMESPACE$9, SWITCH_ORG_MODAL_I18N);
46276
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46277
+ this.i18n.registerContent(DEFAULT_NAMESPACE$a, SWITCH_ORG_MODAL_I18N);
46283
46278
  }
46284
46279
  this.loadOrgs();
46285
46280
  }
@@ -46597,7 +46592,7 @@ const ACCOUNT_VIEW_I18N = {
46597
46592
  };
46598
46593
 
46599
46594
  addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46600
- const DEFAULT_NAMESPACE$8 = 'Settings.Account';
46595
+ const DEFAULT_NAMESPACE$9 = 'Settings.Account';
46601
46596
  const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46602
46597
  /**
46603
46598
  * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
@@ -46638,7 +46633,7 @@ class AccountViewComponent {
46638
46633
  showNewOrgCta: merged.showNewOrgCta ?? true,
46639
46634
  showLogout: merged.showLogout ?? true,
46640
46635
  showDeleteAccount: merged.showDeleteAccount ?? true,
46641
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$8,
46636
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$9,
46642
46637
  manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46643
46638
  onOrgSwitched: merged.onOrgSwitched,
46644
46639
  onOrgCreated: merged.onOrgCreated,
@@ -47363,7 +47358,7 @@ const EDIT_ORG_MODAL_I18N = {
47363
47358
  },
47364
47359
  };
47365
47360
 
47366
- const DEFAULT_NAMESPACE$7 = 'EditOrgModal';
47361
+ const DEFAULT_NAMESPACE$8 = 'EditOrgModal';
47367
47362
  /**
47368
47363
  * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
47369
47364
  * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
@@ -47390,10 +47385,10 @@ class EditOrgModalComponent {
47390
47385
  /** Organización a editar (pasada por `componentProps`). */
47391
47386
  this.org = null;
47392
47387
  /** Namespace i18n con que la vista resuelve sus textos. */
47393
- this.i18nNamespace = DEFAULT_NAMESPACE$7;
47388
+ this.i18nNamespace = DEFAULT_NAMESPACE$8;
47394
47389
  this.saving = signal(false);
47395
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47396
- this.i18n.registerContent(DEFAULT_NAMESPACE$7, EDIT_ORG_MODAL_I18N);
47390
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
47391
+ this.i18n.registerContent(DEFAULT_NAMESPACE$8, EDIT_ORG_MODAL_I18N);
47397
47392
  }
47398
47393
  this.formMeta = signal(this.buildFormMeta());
47399
47394
  effect(() => {
@@ -47631,7 +47626,7 @@ const INVITE_MEMBER_MODAL_I18N = {
47631
47626
  },
47632
47627
  };
47633
47628
 
47634
- const DEFAULT_NAMESPACE$6 = 'InviteModal';
47629
+ const DEFAULT_NAMESPACE$7 = 'InviteModal';
47635
47630
  addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
47636
47631
  /**
47637
47632
  * `val-invite-member-modal` — invitación de miembros a una organización.
@@ -47662,7 +47657,7 @@ class InviteMemberModalComponent {
47662
47657
  /** Ids de miembros ya existentes (excluidos de los resultados). */
47663
47658
  this.existingMemberIds = [];
47664
47659
  /** Namespace i18n con que la vista resuelve sus textos. */
47665
- this.i18nNamespace = DEFAULT_NAMESPACE$6;
47660
+ this.i18nNamespace = DEFAULT_NAMESPACE$7;
47666
47661
  this.query = signal('');
47667
47662
  this.searching = signal(false);
47668
47663
  this.searchResults = signal([]);
@@ -47709,8 +47704,8 @@ class InviteMemberModalComponent {
47709
47704
  state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
47710
47705
  }));
47711
47706
  this.searchSubject = new Subject();
47712
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
47713
- this.i18n.registerContent(DEFAULT_NAMESPACE$6, INVITE_MEMBER_MODAL_I18N);
47707
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47708
+ this.i18n.registerContent(DEFAULT_NAMESPACE$7, INVITE_MEMBER_MODAL_I18N);
47714
47709
  }
47715
47710
  this.loadRoles();
47716
47711
  this.roleSub = this.roleControl.valueChanges.subscribe(v => {
@@ -48199,7 +48194,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
48199
48194
  },
48200
48195
  };
48201
48196
 
48202
- const DEFAULT_NAMESPACE$5 = 'Settings.MemberDetail';
48197
+ const DEFAULT_NAMESPACE$6 = 'Settings.MemberDetail';
48203
48198
  /**
48204
48199
  * `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
48205
48200
  * permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -48236,7 +48231,7 @@ class MemberDetailModalComponent {
48236
48231
  { id: 'admin', name: 'admin' },
48237
48232
  ];
48238
48233
  /** Namespace i18n con que la vista resuelve sus textos. */
48239
- this.i18nNamespace = DEFAULT_NAMESPACE$5;
48234
+ this.i18nNamespace = DEFAULT_NAMESPACE$6;
48240
48235
  this.working = signal(false);
48241
48236
  this.currentRole = signal('');
48242
48237
  this.roleControl = new FormControl('');
@@ -48276,8 +48271,8 @@ class MemberDetailModalComponent {
48276
48271
  type: 'button',
48277
48272
  state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
48278
48273
  }));
48279
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48280
- this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_DETAIL_MODAL_I18N);
48274
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
48275
+ this.i18n.registerContent(DEFAULT_NAMESPACE$6, MEMBER_DETAIL_MODAL_I18N);
48281
48276
  }
48282
48277
  }
48283
48278
  ngOnInit() {
@@ -48614,7 +48609,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
48614
48609
  },
48615
48610
  };
48616
48611
 
48617
- const DEFAULT_NAMESPACE$4 = 'Settings.TransferModal';
48612
+ const DEFAULT_NAMESPACE$5 = 'Settings.TransferModal';
48618
48613
  /**
48619
48614
  * `val-transfer-ownership-modal` — selector de miembro para transferir la
48620
48615
  * propiedad de la organización. Promovido desde `showcase` bajo el proceso de
@@ -48638,7 +48633,7 @@ class TransferOwnershipModalComponent {
48638
48633
  /** Lista de miembros transferibles (todos excepto el owner actual). */
48639
48634
  this.members = [];
48640
48635
  /** Namespace i18n con que la vista resuelve sus textos. */
48641
- this.i18nNamespace = DEFAULT_NAMESPACE$4;
48636
+ this.i18nNamespace = DEFAULT_NAMESPACE$5;
48642
48637
  this.query = signal('');
48643
48638
  this.selectedId = signal('');
48644
48639
  this.filtered = computed(() => {
@@ -48650,8 +48645,8 @@ class TransferOwnershipModalComponent {
48650
48645
  m.userId.toLowerCase().includes(q));
48651
48646
  });
48652
48647
  this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
48653
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
48654
- this.i18n.registerContent(DEFAULT_NAMESPACE$4, TRANSFER_OWNERSHIP_MODAL_I18N);
48648
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48649
+ this.i18n.registerContent(DEFAULT_NAMESPACE$5, TRANSFER_OWNERSHIP_MODAL_I18N);
48655
48650
  }
48656
48651
  }
48657
48652
  ngOnInit() {
@@ -48984,7 +48979,7 @@ const ORGANIZATION_VIEW_I18N = {
48984
48979
  },
48985
48980
  };
48986
48981
 
48987
- const DEFAULT_NAMESPACE$3 = 'Settings.Organization';
48982
+ const DEFAULT_NAMESPACE$4 = 'Settings.Organization';
48988
48983
  const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
48989
48984
  const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
48990
48985
  /**
@@ -49041,7 +49036,7 @@ class OrganizationViewComponent {
49041
49036
  showInvite: merged.showInvite ?? true,
49042
49037
  showTransferOwnership: merged.showTransferOwnership ?? true,
49043
49038
  showLeave: merged.showLeave ?? true,
49044
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
49039
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$4,
49045
49040
  viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
49046
49041
  leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
49047
49042
  onOrgEdited: merged.onOrgEdited,
@@ -50128,7 +50123,7 @@ addIcons({
50128
50123
  notificationsOutline,
50129
50124
  refreshOutline,
50130
50125
  });
50131
- const DEFAULT_NAMESPACE$2 = 'Settings.Notifications';
50126
+ const DEFAULT_NAMESPACE$3 = 'Settings.Notifications';
50132
50127
  /**
50133
50128
  * `val-notification-preferences-view` — **preferencias de notificaciones push**
50134
50129
  * (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
@@ -50171,7 +50166,7 @@ class NotificationPreferencesViewComponent {
50171
50166
  const merged = { ...fromRoute, ...(this.config ?? {}) };
50172
50167
  return {
50173
50168
  showDetails: merged.showDetails ?? true,
50174
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
50169
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
50175
50170
  onPushEnabled: merged.onPushEnabled,
50176
50171
  onPushDisabled: merged.onPushDisabled,
50177
50172
  };
@@ -50942,7 +50937,7 @@ const PERMISSIONS_VIEW_I18N = {
50942
50937
  },
50943
50938
  };
50944
50939
 
50945
- const DEFAULT_NAMESPACE$1 = 'Settings.Permissions';
50940
+ const DEFAULT_NAMESPACE$2 = 'Settings.Permissions';
50946
50941
  /**
50947
50942
  * `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
50948
50943
  * Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
@@ -50982,7 +50977,7 @@ class PermissionsViewComponent {
50982
50977
  return {
50983
50978
  showSystemRoles: merged.showSystemRoles ?? true,
50984
50979
  showCustomRoles: merged.showCustomRoles ?? true,
50985
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
50980
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
50986
50981
  };
50987
50982
  });
50988
50983
  this.loading = signal(false);
@@ -51756,7 +51751,7 @@ addIcons({
51756
51751
  openOutline,
51757
51752
  personOutline,
51758
51753
  });
51759
- const DEFAULT_NAMESPACE = 'Notifications';
51754
+ const DEFAULT_NAMESPACE$1 = 'Notifications';
51760
51755
  const DEFAULT_SETTINGS_PATH = '/app/settings/notifications';
51761
51756
  const DEFAULT_PAGE_SIZE = 10;
51762
51757
  const DEFAULT_MAX_LOAD = 50;
@@ -51816,7 +51811,7 @@ class NotificationsViewComponent {
51816
51811
  showPushOffCta: merged.showPushOffCta ?? true,
51817
51812
  enableMarkAll: merged.enableMarkAll ?? true,
51818
51813
  settingsNotificationsPath: merged.settingsNotificationsPath ?? DEFAULT_SETTINGS_PATH,
51819
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
51814
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
51820
51815
  pageSize: merged.pageSize ?? DEFAULT_PAGE_SIZE,
51821
51816
  maxLoad: merged.maxLoad ?? DEFAULT_MAX_LOAD,
51822
51817
  onNotificationOpen: merged.onNotificationOpen,
@@ -52484,6 +52479,797 @@ function provideValtechNotificationsRoutes(opts) {
52484
52479
  ];
52485
52480
  }
52486
52481
 
52482
+ /**
52483
+ * Defaults i18n del componente (es/en). El componente los auto-registra en su
52484
+ * constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
52485
+ * garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
52486
+ * template por cada change-detection → `console.warn` por CD → con el
52487
+ * `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
52488
+ * infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
52489
+ */
52490
+ const CONTENT_REACTION_I18N = {
52491
+ es: {
52492
+ question: '¿Te resultó útil este contenido?',
52493
+ commentPlaceholder: 'Cuéntanos más (opcional)...',
52494
+ submit: 'Enviar opinión',
52495
+ update: 'Actualizar opinión',
52496
+ thankYou: '¡Gracias por tu opinión!',
52497
+ submitted: 'Tu opinión ha sido registrada',
52498
+ errorSubmitting: 'Error al enviar. Intenta de nuevo.',
52499
+ positiveLabel: 'Sí, mucho',
52500
+ negativeLabel: 'No tanto',
52501
+ negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
52502
+ },
52503
+ en: {
52504
+ question: 'Was this content helpful?',
52505
+ commentPlaceholder: 'Tell us more (optional)...',
52506
+ submit: 'Send feedback',
52507
+ update: 'Update feedback',
52508
+ thankYou: 'Thanks for your feedback!',
52509
+ submitted: 'Your feedback has been recorded',
52510
+ errorSubmitting: 'Failed to send. Please try again.',
52511
+ positiveLabel: 'Yes, a lot',
52512
+ negativeLabel: 'Not really',
52513
+ negativeFeedbackTitle: "Tell us what you didn't like?",
52514
+ },
52515
+ };
52516
+ /**
52517
+ * Componente para reacciones de contenido con emojis.
52518
+ *
52519
+ * @example
52520
+ * ```html
52521
+ * <val-content-reaction
52522
+ * [props]="{
52523
+ * entityRef: { entityType: 'article', entityId: 'art-123' },
52524
+ * question: '¿Te fue útil este artículo?'
52525
+ * }"
52526
+ * (reactionSubmit)="onReactionSubmit($event)"
52527
+ * />
52528
+ * ```
52529
+ */
52530
+ class ContentReactionComponent {
52531
+ constructor() {
52532
+ this.feedbackService = inject(FeedbackService);
52533
+ this.toast = inject(ToastService);
52534
+ this.i18n = inject(I18nService);
52535
+ this.auth = inject(AuthService, { optional: true });
52536
+ this.isConfigured = computed(() => this.feedbackService.isConfigured());
52537
+ this.props = {};
52538
+ this.reactionSubmit = new EventEmitter();
52539
+ this.reactionChange = new EventEmitter();
52540
+ // Estado reactivo
52541
+ this.state = signal({
52542
+ selectedValue: null,
52543
+ comment: '',
52544
+ isLoading: false,
52545
+ isSubmitted: false,
52546
+ hadPreviousReaction: false,
52547
+ error: null,
52548
+ });
52549
+ // Valores por defecto
52550
+ this.defaultEmojis = ['😞', '😐', '😊'];
52551
+ this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
52552
+ // Computed properties
52553
+ this.resolvedProps = computed(() => ({
52554
+ entityRef: this.props.entityRef,
52555
+ question: this.props.question || this.t('question'),
52556
+ showComment: this.props.showComment ?? true,
52557
+ commentOnValues: this.props.commentOnValues,
52558
+ commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
52559
+ maxCommentLength: this.props.maxCommentLength ?? 500,
52560
+ emojis: this.props.emojis || this.defaultEmojis,
52561
+ emojiLabels: this.props.emojiLabels || this.defaultLabels,
52562
+ showThankYou: this.props.showThankYou ?? true,
52563
+ thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
52564
+ disabled: this.props.disabled ?? false,
52565
+ readonly: this.props.readonly ?? false,
52566
+ // Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
52567
+ variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
52568
+ }));
52569
+ this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
52570
+ this.showCommentField = computed(() => {
52571
+ const sel = this.state().selectedValue;
52572
+ if (!sel)
52573
+ return false;
52574
+ const commentOn = this.resolvedProps().commentOnValues;
52575
+ if (commentOn !== undefined)
52576
+ return commentOn.includes(sel);
52577
+ return this.resolvedProps().showComment;
52578
+ });
52579
+ this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
52580
+ // Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
52581
+ // namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
52582
+ // CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
52583
+ if (!this.i18n.hasNamespace('ContentReaction')) {
52584
+ this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
52585
+ }
52586
+ }
52587
+ ngOnInit() {
52588
+ this.loadPreviousReaction();
52589
+ }
52590
+ ngOnChanges(changes) {
52591
+ if (changes['props'] && !changes['props'].firstChange) {
52592
+ this.loadPreviousReaction();
52593
+ }
52594
+ }
52595
+ async loadPreviousReaction() {
52596
+ if (!this.props.entityRef)
52597
+ return;
52598
+ // Si hay initialValue, usarlo directamente sin consultar
52599
+ if (this.props.initialValue) {
52600
+ this.state.update(s => ({
52601
+ ...s,
52602
+ selectedValue: this.props.initialValue,
52603
+ hadPreviousReaction: true,
52604
+ isLoading: false,
52605
+ isSubmitted: true,
52606
+ }));
52607
+ return;
52608
+ }
52609
+ // Si skipInitialCheck está activo, no consultar
52610
+ if (this.props.skipInitialCheck) {
52611
+ return;
52612
+ }
52613
+ this.state.update(s => ({ ...s, isLoading: true, error: null }));
52614
+ try {
52615
+ const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
52616
+ if (check.hasFeedback && check.reactionValue) {
52617
+ this.state.update(s => ({
52618
+ ...s,
52619
+ selectedValue: check.reactionValue,
52620
+ hadPreviousReaction: true,
52621
+ isLoading: false,
52622
+ isSubmitted: true,
52623
+ }));
52624
+ }
52625
+ else {
52626
+ this.state.update(s => ({ ...s, isLoading: false }));
52627
+ }
52628
+ }
52629
+ catch (error) {
52630
+ console.error('Error loading previous reaction:', error);
52631
+ this.state.update(s => ({ ...s, isLoading: false }));
52632
+ }
52633
+ }
52634
+ selectReaction(value) {
52635
+ if (this.resolvedProps().disabled || this.resolvedProps().readonly)
52636
+ return;
52637
+ const previousValue = this.state().selectedValue;
52638
+ this.state.update(s => ({
52639
+ ...s,
52640
+ selectedValue: value,
52641
+ isSubmitted: false,
52642
+ error: null,
52643
+ }));
52644
+ this.reactionChange.emit({ value, previousValue });
52645
+ }
52646
+ async submitReaction() {
52647
+ const currentState = this.state();
52648
+ const props = this.resolvedProps();
52649
+ if (!currentState.selectedValue || props.disabled)
52650
+ return;
52651
+ this.state.update(s => ({ ...s, isLoading: true, error: null }));
52652
+ try {
52653
+ // Determinar si usar endpoint anónimo o autenticado
52654
+ const isAuthenticated = this.auth?.isAuthenticated() ?? false;
52655
+ const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
52656
+ if (useAnonymous) {
52657
+ // Usar endpoint anónimo (público)
52658
+ await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
52659
+ }
52660
+ else {
52661
+ // Usar endpoint autenticado
52662
+ await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
52663
+ }
52664
+ this.state.update(s => ({
52665
+ ...s,
52666
+ isLoading: false,
52667
+ isSubmitted: true,
52668
+ comment: '',
52669
+ hadPreviousReaction: !useAnonymous, // Solo para autenticados
52670
+ }));
52671
+ this.reactionSubmit.emit({
52672
+ value: currentState.selectedValue,
52673
+ comment: currentState.comment || undefined,
52674
+ entityRef: props.entityRef,
52675
+ isUpdate: currentState.hadPreviousReaction,
52676
+ });
52677
+ if (props.showThankYou) {
52678
+ this.toast.show({
52679
+ message: props.thankYouMessage,
52680
+ duration: 2000,
52681
+ });
52682
+ }
52683
+ }
52684
+ catch (error) {
52685
+ console.error('Error submitting reaction:', error);
52686
+ this.state.update(s => ({
52687
+ ...s,
52688
+ isLoading: false,
52689
+ error: this.t('errorSubmitting'),
52690
+ }));
52691
+ this.toast.show({
52692
+ message: this.t('errorSubmitting'),
52693
+ duration: 3000,
52694
+ });
52695
+ }
52696
+ }
52697
+ updateComment(event) {
52698
+ const value = event.detail.value || '';
52699
+ this.state.update(s => ({ ...s, comment: value }));
52700
+ }
52701
+ getEmoji(index) {
52702
+ return this.resolvedProps().emojis[index];
52703
+ }
52704
+ getEmojiLabel(index) {
52705
+ return this.resolvedProps().emojiLabels[index];
52706
+ }
52707
+ isSelected(value) {
52708
+ return this.state().selectedValue === value;
52709
+ }
52710
+ t(key) {
52711
+ const translations = {
52712
+ question: '¿Te resultó útil este contenido?',
52713
+ commentPlaceholder: 'Cuéntanos más (opcional)...',
52714
+ submit: 'Enviar opinión',
52715
+ update: 'Actualizar opinión',
52716
+ thankYou: '¡Gracias por tu opinión!',
52717
+ submitted: 'Tu opinión ha sido registrada',
52718
+ errorSubmitting: 'Error al enviar. Intenta de nuevo.',
52719
+ positiveLabel: 'Sí, mucho',
52720
+ negativeLabel: 'No tanto',
52721
+ negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
52722
+ };
52723
+ return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
52724
+ }
52725
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52726
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
52727
+ }
52728
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
52729
+ type: Component,
52730
+ args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
52731
+ }], ctorParameters: () => [], propDecorators: { props: [{
52732
+ type: Input
52733
+ }], reactionSubmit: [{
52734
+ type: Output
52735
+ }], reactionChange: [{
52736
+ type: Output
52737
+ }] } });
52738
+
52739
+ /**
52740
+ * Defaults i18n (es/en) embebidos en `val-about-view`. Auto-registrados en el
52741
+ * constructor del componente si el consumer no proveyó el namespace
52742
+ * (`Settings.About` por default). SOLO incluye las keys que la vista usa.
52743
+ *
52744
+ * Textos portados EXACTOS del override `Settings.About` de showcase
52745
+ * (`frontend/showcase/src/i18n/index.ts`). Las keys de las acciones
52746
+ * showcase-specific (`feedback*`, `share*`, `faq*`, `donate*`) NO se incluyen:
52747
+ * esas acciones se modelan ahora como `actions[]` de la config (el consumer
52748
+ * aporta sus propios textos). `copyrightText` se incluye aquí (antes salía del
52749
+ * namespace `Layout`) para que la vista sea autocontenida.
52750
+ */
52751
+ const ABOUT_VIEW_I18N = {
52752
+ es: {
52753
+ pageTitle: 'Acerca de',
52754
+ pageDescription: 'Demo del factory Valtech',
52755
+ tagline: 'App de demo — explora las funcionalidades en vivo',
52756
+ descTitle: 'Acerca de Showcase',
52757
+ descBody: 'Showcase es la app de referencia del factory Valtech. Aquí puedes explorar el flujo completo en vivo: autenticación, perfil, organizaciones, notificaciones y ajustes de cuenta.',
52758
+ actionsTitle: 'Acciones',
52759
+ rateTitle: 'Calificar la app',
52760
+ rateDesc: 'Cuéntanos qué te ha parecido la experiencia.',
52761
+ rateModalTitle: 'Tu opinión',
52762
+ rateQuestion: '¿Qué te ha parecido la experiencia hasta ahora?',
52763
+ rateComment: 'Cuéntanos más (opcional)…',
52764
+ rateThanks: '¡Gracias por tu opinión!',
52765
+ rateClose: 'Cerrar',
52766
+ rateBad: 'No tanto',
52767
+ rateMid: 'Bien',
52768
+ rateGood: '¡Me encanta!',
52769
+ socialTitle: 'Síguenos',
52770
+ socialDesc: 'Estamos en redes sociales.',
52771
+ versionLabel: 'Versión',
52772
+ versionCopied: 'v{v} copiado',
52773
+ copyrightText: '© {year}, Soluciones Valtech Ltda. o sus afiliados. Todos los derechos reservados.',
52774
+ },
52775
+ en: {
52776
+ pageTitle: 'About',
52777
+ pageDescription: 'Valtech factory demo',
52778
+ tagline: 'Demo app — explore features live',
52779
+ descTitle: 'About Showcase',
52780
+ descBody: 'Showcase is the reference app for the Valtech factory. Explore the full flow live: authentication, profile, organizations, notifications and account settings.',
52781
+ actionsTitle: 'Actions',
52782
+ rateTitle: 'Rate the app',
52783
+ rateDesc: 'Tell us how your experience has been.',
52784
+ rateModalTitle: 'Your feedback',
52785
+ rateQuestion: 'How has your experience been so far?',
52786
+ rateComment: 'Tell us more (optional)…',
52787
+ rateThanks: 'Thanks for your feedback!',
52788
+ rateClose: 'Close',
52789
+ rateBad: 'Not great',
52790
+ rateMid: 'Good',
52791
+ rateGood: 'Love it!',
52792
+ socialTitle: 'Follow us',
52793
+ socialDesc: "We're on social media.",
52794
+ versionLabel: 'Version',
52795
+ versionCopied: 'v{v} copied',
52796
+ copyrightText: '© {year}, Soluciones Valtech Ltda. or its affiliates. All rights reserved.',
52797
+ },
52798
+ };
52799
+
52800
+ addIcons({
52801
+ copyOutline,
52802
+ starOutline,
52803
+ chatbubbleEllipsesOutline,
52804
+ shareSocialOutline,
52805
+ helpCircleOutline,
52806
+ logoFacebook,
52807
+ logoInstagram,
52808
+ logoLinkedin,
52809
+ logoTwitter,
52810
+ logoYoutube,
52811
+ logoTiktok,
52812
+ });
52813
+ const DEFAULT_NAMESPACE = 'Settings.About';
52814
+ const DEFAULT_BRAND_NAME = 'Valtech';
52815
+ const DEFAULT_LOGO_SRC = 'assets/images/main-icon.png';
52816
+ const DEFAULT_RATING_ENTITY_ID = 'experience';
52817
+ /**
52818
+ * `val-about-view` — vista About full-feature autocontenida (organism).
52819
+ * Promovida desde `showcase` bajo el proceso de ADR-021. Reúne:
52820
+ * - **HERO** — logo + nombre de marca + tagline + pill de versión (copiable).
52821
+ * - **Descripción** — título + cuerpo.
52822
+ * - **Acciones** — action-card de rating built-in (abre `val-content-reaction`
52823
+ * DIRECTAMENTE vía `ModalService`, sin wrapper local — decisión CTO) + las
52824
+ * acciones genéricas que el consumer aporte por `config.actions`.
52825
+ * - **Redes** — links sociales (`config.socialLinks`); sección oculta si vacía.
52826
+ * - **Footer** — copyright con el año actual.
52827
+ *
52828
+ * Lo app-specific (logo, nombre, versión, redes, acciones share/faq) viaja como
52829
+ * DATA de config — NO como slots (ADR-021). La versión la pasa el consumer
52830
+ * (`config.version` = su `APP_VERSION`); la lib NO importa el `version.ts` de la
52831
+ * app. Si no se pasa versión, el pill se oculta.
52832
+ *
52833
+ * NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute`
52834
+ * se inyecta `{ optional: true }` solo para leer el route data de config.
52835
+ *
52836
+ * Auto-registra sus defaults i18n (es/en) en el constructor si el consumer no
52837
+ * proveyó el namespace configurado (default `Settings.About`).
52838
+ *
52839
+ * GOTCHA i18n ContentReaction: `val-content-reaction` exige su namespace
52840
+ * `ContentReaction` registrado donde se use (key faltante en binding + CD =
52841
+ * navegador congelado en dev). `ContentReactionComponent` se auto-registra ese
52842
+ * namespace en SU constructor, y como se abre vía `ModalService` ese constructor
52843
+ * corre antes de que su template binde — queda cubierto. Igual lo importamos
52844
+ * como dependencia explícita de esta vista para que la garantía sea local.
52845
+ */
52846
+ class AboutViewComponent {
52847
+ /** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
52848
+ get ns() {
52849
+ return this.resolvedConfig().i18nNamespace;
52850
+ }
52851
+ constructor() {
52852
+ this.nav = inject(NavigationService);
52853
+ this.i18n = inject(I18nService);
52854
+ this.modalService = inject(ModalService);
52855
+ this.toast = inject(ToastService);
52856
+ this.route = inject(ActivatedRoute, { optional: true });
52857
+ this.resolvedConfig = computed(() => {
52858
+ const fromRoute = (this.route?.snapshot.data['aboutConfig'] ?? {});
52859
+ const merged = { ...fromRoute, ...(this.config ?? {}) };
52860
+ return {
52861
+ brand: {
52862
+ name: merged.brand?.name ?? DEFAULT_BRAND_NAME,
52863
+ logoSrc: merged.brand?.logoSrc ?? DEFAULT_LOGO_SRC,
52864
+ },
52865
+ version: merged.version ?? '',
52866
+ socialLinks: merged.socialLinks ?? [],
52867
+ showRating: merged.showRating ?? true,
52868
+ ratingEntityId: merged.ratingEntityId ?? DEFAULT_RATING_ENTITY_ID,
52869
+ actions: merged.actions ?? [],
52870
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
52871
+ onRated: merged.onRated,
52872
+ };
52873
+ });
52874
+ this.brandName = computed(() => this.resolvedConfig().brand.name);
52875
+ this.logoSrc = computed(() => this.resolvedConfig().brand.logoSrc);
52876
+ this.version = computed(() => this.resolvedConfig().version);
52877
+ this.socialLinks = computed(() => this.resolvedConfig().socialLinks);
52878
+ this.showRating = computed(() => this.resolvedConfig().showRating);
52879
+ this.actions = computed(() => this.resolvedConfig().actions);
52880
+ /** Copyright del footer — año actual interpolado en `copyrightText`. */
52881
+ this.copyright = computed(() => {
52882
+ this.i18n.lang();
52883
+ const year = String(new Date().getFullYear());
52884
+ return this.tt('copyrightText').replace('{year}', year);
52885
+ });
52886
+ /** Title metadata para secciones — bold + size small. */
52887
+ this.rateActionCard = computed(() => {
52888
+ this.i18n.lang();
52889
+ return {
52890
+ icon: { ionicon: 'star-outline', color: 'primary' },
52891
+ title: this.tt('rateTitle'),
52892
+ description: this.tt('rateDesc'),
52893
+ size: 'medium',
52894
+ bordered: false,
52895
+ shadowed: false,
52896
+ };
52897
+ });
52898
+ const ns = this.ns;
52899
+ if (!this.i18n.hasNamespace(ns)) {
52900
+ this.i18n.registerContent(ns, ABOUT_VIEW_I18N);
52901
+ }
52902
+ this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
52903
+ }
52904
+ /** Helper i18n bound al namespace resuelto. Reactivo vía `lang()`. */
52905
+ tt(key) {
52906
+ this.i18n.lang();
52907
+ return this.i18n.t(key, this.ns);
52908
+ }
52909
+ /** Title metadata para secciones — bold + size small. */
52910
+ sectionTitle(key) {
52911
+ this.i18n.lang();
52912
+ return {
52913
+ content: this.tt(key),
52914
+ size: 'small',
52915
+ color: 'dark',
52916
+ bold: true,
52917
+ };
52918
+ }
52919
+ /** ActionCardMetadata para una acción genérica del consumer. */
52920
+ extraActionCard(action) {
52921
+ return {
52922
+ icon: { ionicon: action.icon ?? 'chatbubble-ellipses-outline', color: 'primary' },
52923
+ title: action.label,
52924
+ description: action.description,
52925
+ size: 'medium',
52926
+ bordered: false,
52927
+ shadowed: false,
52928
+ };
52929
+ }
52930
+ /** Copia la versión al portapapeles + toast (color dark). */
52931
+ async copyVersion() {
52932
+ const v = this.version();
52933
+ if (!v)
52934
+ return;
52935
+ try {
52936
+ await navigator.clipboard.writeText(v);
52937
+ this.toast.show({
52938
+ message: this.tt('versionCopied').replace('{v}', v),
52939
+ color: 'dark',
52940
+ duration: 1500,
52941
+ });
52942
+ }
52943
+ catch {
52944
+ // clipboard no disponible — silencioso
52945
+ }
52946
+ }
52947
+ /** Abre una URL externa en nueva tab. `mailto:` queda en la misma tab. */
52948
+ openUrl(url) {
52949
+ if (url.startsWith('mailto:')) {
52950
+ window.location.href = url;
52951
+ return;
52952
+ }
52953
+ window.open(url, '_blank', 'noopener,noreferrer');
52954
+ }
52955
+ /**
52956
+ * "Calificar la app" — abre `val-content-reaction` DIRECTAMENTE como sheet
52957
+ * modal (sin wrapper local — decisión CTO). El componente persiste la reacción
52958
+ * contra el backend de feedback y muestra su propio estado de "gracias"; el
52959
+ * sheet se cierra arrastrando (breakpoint 0). El namespace i18n
52960
+ * `ContentReaction` lo auto-registra `ContentReactionComponent` en su
52961
+ * constructor, que corre al crear el modal — sin riesgo de key faltante.
52962
+ *
52963
+ * Sheet (bottom sheet): el contenido scrollea dentro y el botón "Enviar" queda
52964
+ * siempre alcanzable, también en viewports chicos.
52965
+ */
52966
+ async openRating() {
52967
+ const reactionProps = {
52968
+ // entityType "app" distingue esta reacción de las de artículos/FAQs.
52969
+ entityRef: { entityType: 'app', entityId: this.resolvedConfig().ratingEntityId },
52970
+ question: this.tt('rateQuestion'),
52971
+ commentPlaceholder: this.tt('rateComment'),
52972
+ thankYouMessage: this.tt('rateThanks'),
52973
+ showComment: true,
52974
+ showThankYou: true,
52975
+ // Modal one-shot: NO bloquear el render con el check de rating previo.
52976
+ skipInitialCheck: true,
52977
+ emojis: ['😕', '🙂', '😍'],
52978
+ emojiLabels: [this.tt('rateBad'), this.tt('rateMid'), this.tt('rateGood')],
52979
+ };
52980
+ await this.modalService.open({
52981
+ component: ContentReactionComponent,
52982
+ componentProps: { props: reactionProps },
52983
+ breakpoints: {
52984
+ initialBreakpoint: 0.9,
52985
+ breakpoints: [0, 0.9, 1],
52986
+ showHandle: true,
52987
+ handleBehavior: 'cycle',
52988
+ },
52989
+ });
52990
+ this.resolvedConfig().onRated?.();
52991
+ }
52992
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
52993
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AboutViewComponent, isStandalone: true, selector: "val-about-view", inputs: { config: "config" }, ngImport: i0, template: `
52994
+ <ion-grid class="about-grid">
52995
+ <!-- HERO: logo + name + version -->
52996
+ <div class="about-hero">
52997
+ <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
52998
+ <val-title
52999
+ [props]="{
53000
+ content: brandName(),
53001
+ size: 'xlarge',
53002
+ color: 'dark',
53003
+ bold: true,
53004
+ }"
53005
+ />
53006
+ <val-text
53007
+ [props]="{
53008
+ content: tt('tagline'),
53009
+ size: 'medium',
53010
+ color: 'dark',
53011
+ bold: false,
53012
+ }"
53013
+ />
53014
+ @if (version()) {
53015
+ <div
53016
+ class="version-row"
53017
+ (click)="copyVersion()"
53018
+ role="button"
53019
+ [attr.aria-label]="tt('versionLabel') + ' ' + version()"
53020
+ >
53021
+ <val-pill
53022
+ [props]="{
53023
+ label: tt('versionLabel') + ' ' + version(),
53024
+ color: 'dark',
53025
+ weight: 'bold',
53026
+ size: 'small',
53027
+ }"
53028
+ />
53029
+ <ion-icon name="copy-outline" class="version-copy-icon" />
53030
+ </div>
53031
+ }
53032
+ </div>
53033
+
53034
+ <!-- DESCRIPTION -->
53035
+ <section class="about-section">
53036
+ <val-title [props]="sectionTitle('descTitle')" />
53037
+ <val-text
53038
+ [props]="{
53039
+ content: tt('descBody'),
53040
+ size: 'medium',
53041
+ color: 'dark',
53042
+ bold: false,
53043
+ }"
53044
+ />
53045
+ </section>
53046
+
53047
+ <!-- ACTIONS — rating built-in + acciones del consumer. -->
53048
+ @if (showRating() || actions().length > 0) {
53049
+ <section class="about-section about-section--reviews">
53050
+ <val-title [props]="sectionTitle('actionsTitle')" />
53051
+ <div class="about-actions">
53052
+ @if (showRating()) {
53053
+ <val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
53054
+ }
53055
+ @for (action of actions(); track action.label) {
53056
+ <val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
53057
+ }
53058
+ </div>
53059
+ </section>
53060
+ }
53061
+
53062
+ <!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
53063
+ @if (socialLinks().length > 0) {
53064
+ <section class="about-section">
53065
+ <val-title [props]="sectionTitle('socialTitle')" />
53066
+ <val-text
53067
+ [props]="{
53068
+ content: tt('socialDesc'),
53069
+ size: 'medium',
53070
+ color: 'dark',
53071
+ bold: false,
53072
+ }"
53073
+ />
53074
+ <div class="about-social">
53075
+ @for (s of socialLinks(); track s.url) {
53076
+ <ion-button
53077
+ fill="clear"
53078
+ size="small"
53079
+ class="social-btn"
53080
+ [attr.aria-label]="s.label"
53081
+ (click)="openUrl(s.url)"
53082
+ >
53083
+ <ion-icon [name]="s.icon" slot="icon-only" />
53084
+ </ion-button>
53085
+ }
53086
+ </div>
53087
+ </section>
53088
+ }
53089
+
53090
+ <!-- FOOTER -->
53091
+ <footer class="about-footer">
53092
+ <val-text
53093
+ [props]="{
53094
+ content: copyright(),
53095
+ size: 'small',
53096
+ color: 'dark',
53097
+ bold: false,
53098
+ }"
53099
+ />
53100
+ </footer>
53101
+ </ion-grid>
53102
+ `, isInline: true, styles: [":host{display:block}.about-grid{padding:16px 0;max-width:720px;margin:0 auto}.about-hero{display:flex;flex-direction:column;align-items:center;gap:8px;padding:24px 0 16px;text-align:center}.about-hero__logo{height:96px;width:auto;display:block}.version-row{display:flex;align-items:center;gap:6px;cursor:pointer;opacity:.85;transition:opacity .15s ease}.version-row:hover{opacity:1}.version-copy-icon{font-size:14px;color:var(--ion-color-medium)}.about-section{padding:16px 0 8px;display:flex;flex-direction:column;gap:8px}.about-actions{display:flex;flex-direction:column;gap:8px}.about-section--reviews val-action-card{display:block}.about-section--reviews val-action-card ::ng-deep .action-card{background:var(--ion-color-light, #f4f5f8);box-shadow:none}.about-section--reviews val-action-card ::ng-deep .action-card__description{color:var(--ion-color-dark);opacity:.7}.about-social{display:flex;flex-wrap:wrap;gap:10px;padding-top:8px}.about-social .social-btn{--border-radius: 50%;--background: var(--ion-color-light, #f4f5f8);--color: var(--ion-color-dark);--padding-start: 0;--padding-end: 0;width:48px;height:48px;margin:0;transition:transform .15s ease}.about-social .social-btn:hover{--background: var(--ion-color-primary-tint, #8a4ce5);--color: #fff;transform:translateY(-2px)}.about-social .social-btn ion-icon{font-size:22px}.about-footer{display:flex;flex-direction:column;align-items:center;gap:4px;padding:24px 0;text-align:center}:host-context(body.dark) val-action-card ::ng-deep .action-card,:host-context(html.ion-palette-dark) val-action-card ::ng-deep .action-card,:host-context([data-theme=\"dark\"]) val-action-card ::ng-deep .action-card{background:#ffffff0a}:host-context(body.dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context(html.ion-palette-dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context([data-theme=\"dark\"]) .about-section--reviews val-action-card ::ng-deep .action-card:hover{background:#ffffff14}:host-context(body.dark) .about-social .social-btn,:host-context(html.ion-palette-dark) .about-social .social-btn,:host-context([data-theme=\"dark\"]) .about-social .social-btn{--background: rgba(255, 255, 255, .04)}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: PillComponent, selector: "val-pill", inputs: ["preset", "props"], outputs: ["pillClick", "pillAction"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ActionCardComponent, selector: "val-action-card", inputs: ["props"], outputs: ["onClick"] }] }); }
53103
+ }
53104
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, decorators: [{
53105
+ type: Component,
53106
+ args: [{ selector: 'val-about-view', standalone: true, imports: [IonButton, IonGrid, IonIcon, PillComponent, TitleComponent, TextComponent, ActionCardComponent], template: `
53107
+ <ion-grid class="about-grid">
53108
+ <!-- HERO: logo + name + version -->
53109
+ <div class="about-hero">
53110
+ <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
53111
+ <val-title
53112
+ [props]="{
53113
+ content: brandName(),
53114
+ size: 'xlarge',
53115
+ color: 'dark',
53116
+ bold: true,
53117
+ }"
53118
+ />
53119
+ <val-text
53120
+ [props]="{
53121
+ content: tt('tagline'),
53122
+ size: 'medium',
53123
+ color: 'dark',
53124
+ bold: false,
53125
+ }"
53126
+ />
53127
+ @if (version()) {
53128
+ <div
53129
+ class="version-row"
53130
+ (click)="copyVersion()"
53131
+ role="button"
53132
+ [attr.aria-label]="tt('versionLabel') + ' ' + version()"
53133
+ >
53134
+ <val-pill
53135
+ [props]="{
53136
+ label: tt('versionLabel') + ' ' + version(),
53137
+ color: 'dark',
53138
+ weight: 'bold',
53139
+ size: 'small',
53140
+ }"
53141
+ />
53142
+ <ion-icon name="copy-outline" class="version-copy-icon" />
53143
+ </div>
53144
+ }
53145
+ </div>
53146
+
53147
+ <!-- DESCRIPTION -->
53148
+ <section class="about-section">
53149
+ <val-title [props]="sectionTitle('descTitle')" />
53150
+ <val-text
53151
+ [props]="{
53152
+ content: tt('descBody'),
53153
+ size: 'medium',
53154
+ color: 'dark',
53155
+ bold: false,
53156
+ }"
53157
+ />
53158
+ </section>
53159
+
53160
+ <!-- ACTIONS — rating built-in + acciones del consumer. -->
53161
+ @if (showRating() || actions().length > 0) {
53162
+ <section class="about-section about-section--reviews">
53163
+ <val-title [props]="sectionTitle('actionsTitle')" />
53164
+ <div class="about-actions">
53165
+ @if (showRating()) {
53166
+ <val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
53167
+ }
53168
+ @for (action of actions(); track action.label) {
53169
+ <val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
53170
+ }
53171
+ </div>
53172
+ </section>
53173
+ }
53174
+
53175
+ <!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
53176
+ @if (socialLinks().length > 0) {
53177
+ <section class="about-section">
53178
+ <val-title [props]="sectionTitle('socialTitle')" />
53179
+ <val-text
53180
+ [props]="{
53181
+ content: tt('socialDesc'),
53182
+ size: 'medium',
53183
+ color: 'dark',
53184
+ bold: false,
53185
+ }"
53186
+ />
53187
+ <div class="about-social">
53188
+ @for (s of socialLinks(); track s.url) {
53189
+ <ion-button
53190
+ fill="clear"
53191
+ size="small"
53192
+ class="social-btn"
53193
+ [attr.aria-label]="s.label"
53194
+ (click)="openUrl(s.url)"
53195
+ >
53196
+ <ion-icon [name]="s.icon" slot="icon-only" />
53197
+ </ion-button>
53198
+ }
53199
+ </div>
53200
+ </section>
53201
+ }
53202
+
53203
+ <!-- FOOTER -->
53204
+ <footer class="about-footer">
53205
+ <val-text
53206
+ [props]="{
53207
+ content: copyright(),
53208
+ size: 'small',
53209
+ color: 'dark',
53210
+ bold: false,
53211
+ }"
53212
+ />
53213
+ </footer>
53214
+ </ion-grid>
53215
+ `, styles: [":host{display:block}.about-grid{padding:16px 0;max-width:720px;margin:0 auto}.about-hero{display:flex;flex-direction:column;align-items:center;gap:8px;padding:24px 0 16px;text-align:center}.about-hero__logo{height:96px;width:auto;display:block}.version-row{display:flex;align-items:center;gap:6px;cursor:pointer;opacity:.85;transition:opacity .15s ease}.version-row:hover{opacity:1}.version-copy-icon{font-size:14px;color:var(--ion-color-medium)}.about-section{padding:16px 0 8px;display:flex;flex-direction:column;gap:8px}.about-actions{display:flex;flex-direction:column;gap:8px}.about-section--reviews val-action-card{display:block}.about-section--reviews val-action-card ::ng-deep .action-card{background:var(--ion-color-light, #f4f5f8);box-shadow:none}.about-section--reviews val-action-card ::ng-deep .action-card__description{color:var(--ion-color-dark);opacity:.7}.about-social{display:flex;flex-wrap:wrap;gap:10px;padding-top:8px}.about-social .social-btn{--border-radius: 50%;--background: var(--ion-color-light, #f4f5f8);--color: var(--ion-color-dark);--padding-start: 0;--padding-end: 0;width:48px;height:48px;margin:0;transition:transform .15s ease}.about-social .social-btn:hover{--background: var(--ion-color-primary-tint, #8a4ce5);--color: #fff;transform:translateY(-2px)}.about-social .social-btn ion-icon{font-size:22px}.about-footer{display:flex;flex-direction:column;align-items:center;gap:4px;padding:24px 0;text-align:center}:host-context(body.dark) val-action-card ::ng-deep .action-card,:host-context(html.ion-palette-dark) val-action-card ::ng-deep .action-card,:host-context([data-theme=\"dark\"]) val-action-card ::ng-deep .action-card{background:#ffffff0a}:host-context(body.dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context(html.ion-palette-dark) .about-section--reviews val-action-card ::ng-deep .action-card:hover,:host-context([data-theme=\"dark\"]) .about-section--reviews val-action-card ::ng-deep .action-card:hover{background:#ffffff14}:host-context(body.dark) .about-social .social-btn,:host-context(html.ion-palette-dark) .about-social .social-btn,:host-context([data-theme=\"dark\"]) .about-social .social-btn{--background: rgba(255, 255, 255, .04)}\n"] }]
53216
+ }], ctorParameters: () => [], propDecorators: { config: [{
53217
+ type: Input
53218
+ }] } });
53219
+
53220
+ /**
53221
+ * Configuración acotada de `val-about-view` (object-first). La vista About es
53222
+ * app-specific por naturaleza (logo, nombre, versión, redes, acciones extra),
53223
+ * así que lo propio de cada app se parametriza como **DATA de config** — NO como
53224
+ * slots arbitrarios (ADR-021: una vista full-feature no abre slots; si falta
53225
+ * algo, se promueve un nuevo punto de extensión).
53226
+ *
53227
+ * Ejes de variación permitidos:
53228
+ * - **branding** — `brand.name` / `brand.logoSrc` (qué muestra el HERO).
53229
+ * - **versión** — `version` la pasa el consumer (la lib NO puede importar el
53230
+ * `version.ts` de la app). Si no se pasa, se oculta el pill de versión.
53231
+ * - **redes** — `socialLinks`; si está vacío se oculta la sección social.
53232
+ * - **rating** — `showRating` + `ratingEntityId` + `onRated`.
53233
+ * - **acciones extra** — `actions[]` genéricas que el consumer agrega junto a
53234
+ * la acción de rating built-in.
53235
+ * - **branding i18n** — `i18nNamespace` (default `'Settings.About'`).
53236
+ */
53237
+
53238
+ /**
53239
+ * Helper para montar la vista About (`val-about-view`) como ruta en una app del
53240
+ * factory. El `config` se pasa por route `data` (`aboutConfig`) y el componente
53241
+ * lo lee como fallback de su `@Input() config` (el `@Input` gana).
53242
+ *
53243
+ * @example
53244
+ * ```ts
53245
+ * // settings.routes.ts
53246
+ * import { APP_VERSION } from '../version';
53247
+ *
53248
+ * export const settingsRoutes: Routes = [
53249
+ * ...provideValtechAboutRoutes({ config: { version: APP_VERSION } }), // → /app/about
53250
+ * { path: 'preferences', loadComponent: () => ... },
53251
+ * ];
53252
+ *
53253
+ * // con redes y acciones extra:
53254
+ * ...provideValtechAboutRoutes({
53255
+ * config: {
53256
+ * version: APP_VERSION,
53257
+ * socialLinks: VALTECH_SOCIAL_LINKS.map(s => ({ icon: s.icon, url: s.url, label: s.name })),
53258
+ * actions: [{ label: 'Compartir', icon: 'share-social-outline', onClick: () => share() }],
53259
+ * },
53260
+ * }),
53261
+ * ```
53262
+ */
53263
+ function provideValtechAboutRoutes(opts) {
53264
+ return [
53265
+ {
53266
+ path: opts?.path ?? 'about',
53267
+ component: AboutViewComponent,
53268
+ data: { aboutConfig: opts?.config },
53269
+ },
53270
+ ];
53271
+ }
53272
+
52487
53273
  /** Built-in label sets for the three platform locales. */
52488
53274
  const CALLOUT_LABELS = {
52489
53275
  es: {
@@ -58581,263 +59367,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
58581
59367
  type: Output
58582
59368
  }] } });
58583
59369
 
58584
- /**
58585
- * Defaults i18n del componente (es/en). El componente los auto-registra en su
58586
- * constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
58587
- * garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
58588
- * template por cada change-detection → `console.warn` por CD → con el
58589
- * `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
58590
- * infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
58591
- */
58592
- const CONTENT_REACTION_I18N = {
58593
- es: {
58594
- question: '¿Te resultó útil este contenido?',
58595
- commentPlaceholder: 'Cuéntanos más (opcional)...',
58596
- submit: 'Enviar opinión',
58597
- update: 'Actualizar opinión',
58598
- thankYou: '¡Gracias por tu opinión!',
58599
- submitted: 'Tu opinión ha sido registrada',
58600
- errorSubmitting: 'Error al enviar. Intenta de nuevo.',
58601
- positiveLabel: 'Sí, mucho',
58602
- negativeLabel: 'No tanto',
58603
- negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
58604
- },
58605
- en: {
58606
- question: 'Was this content helpful?',
58607
- commentPlaceholder: 'Tell us more (optional)...',
58608
- submit: 'Send feedback',
58609
- update: 'Update feedback',
58610
- thankYou: 'Thanks for your feedback!',
58611
- submitted: 'Your feedback has been recorded',
58612
- errorSubmitting: 'Failed to send. Please try again.',
58613
- positiveLabel: 'Yes, a lot',
58614
- negativeLabel: 'Not really',
58615
- negativeFeedbackTitle: "Tell us what you didn't like?",
58616
- },
58617
- };
58618
- /**
58619
- * Componente para reacciones de contenido con emojis.
58620
- *
58621
- * @example
58622
- * ```html
58623
- * <val-content-reaction
58624
- * [props]="{
58625
- * entityRef: { entityType: 'article', entityId: 'art-123' },
58626
- * question: '¿Te fue útil este artículo?'
58627
- * }"
58628
- * (reactionSubmit)="onReactionSubmit($event)"
58629
- * />
58630
- * ```
58631
- */
58632
- class ContentReactionComponent {
58633
- constructor() {
58634
- this.feedbackService = inject(FeedbackService);
58635
- this.toast = inject(ToastService);
58636
- this.i18n = inject(I18nService);
58637
- this.auth = inject(AuthService, { optional: true });
58638
- this.isConfigured = computed(() => this.feedbackService.isConfigured());
58639
- this.props = {};
58640
- this.reactionSubmit = new EventEmitter();
58641
- this.reactionChange = new EventEmitter();
58642
- // Estado reactivo
58643
- this.state = signal({
58644
- selectedValue: null,
58645
- comment: '',
58646
- isLoading: false,
58647
- isSubmitted: false,
58648
- hadPreviousReaction: false,
58649
- error: null,
58650
- });
58651
- // Valores por defecto
58652
- this.defaultEmojis = ['😞', '😐', '😊'];
58653
- this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
58654
- // Computed properties
58655
- this.resolvedProps = computed(() => ({
58656
- entityRef: this.props.entityRef,
58657
- question: this.props.question || this.t('question'),
58658
- showComment: this.props.showComment ?? true,
58659
- commentOnValues: this.props.commentOnValues,
58660
- commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
58661
- maxCommentLength: this.props.maxCommentLength ?? 500,
58662
- emojis: this.props.emojis || this.defaultEmojis,
58663
- emojiLabels: this.props.emojiLabels || this.defaultLabels,
58664
- showThankYou: this.props.showThankYou ?? true,
58665
- thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
58666
- disabled: this.props.disabled ?? false,
58667
- readonly: this.props.readonly ?? false,
58668
- // Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
58669
- variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
58670
- }));
58671
- this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
58672
- this.showCommentField = computed(() => {
58673
- const sel = this.state().selectedValue;
58674
- if (!sel)
58675
- return false;
58676
- const commentOn = this.resolvedProps().commentOnValues;
58677
- if (commentOn !== undefined)
58678
- return commentOn.includes(sel);
58679
- return this.resolvedProps().showComment;
58680
- });
58681
- this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
58682
- // Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
58683
- // namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
58684
- // CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
58685
- if (!this.i18n.hasNamespace('ContentReaction')) {
58686
- this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
58687
- }
58688
- }
58689
- ngOnInit() {
58690
- this.loadPreviousReaction();
58691
- }
58692
- ngOnChanges(changes) {
58693
- if (changes['props'] && !changes['props'].firstChange) {
58694
- this.loadPreviousReaction();
58695
- }
58696
- }
58697
- async loadPreviousReaction() {
58698
- if (!this.props.entityRef)
58699
- return;
58700
- // Si hay initialValue, usarlo directamente sin consultar
58701
- if (this.props.initialValue) {
58702
- this.state.update(s => ({
58703
- ...s,
58704
- selectedValue: this.props.initialValue,
58705
- hadPreviousReaction: true,
58706
- isLoading: false,
58707
- isSubmitted: true,
58708
- }));
58709
- return;
58710
- }
58711
- // Si skipInitialCheck está activo, no consultar
58712
- if (this.props.skipInitialCheck) {
58713
- return;
58714
- }
58715
- this.state.update(s => ({ ...s, isLoading: true, error: null }));
58716
- try {
58717
- const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
58718
- if (check.hasFeedback && check.reactionValue) {
58719
- this.state.update(s => ({
58720
- ...s,
58721
- selectedValue: check.reactionValue,
58722
- hadPreviousReaction: true,
58723
- isLoading: false,
58724
- isSubmitted: true,
58725
- }));
58726
- }
58727
- else {
58728
- this.state.update(s => ({ ...s, isLoading: false }));
58729
- }
58730
- }
58731
- catch (error) {
58732
- console.error('Error loading previous reaction:', error);
58733
- this.state.update(s => ({ ...s, isLoading: false }));
58734
- }
58735
- }
58736
- selectReaction(value) {
58737
- if (this.resolvedProps().disabled || this.resolvedProps().readonly)
58738
- return;
58739
- const previousValue = this.state().selectedValue;
58740
- this.state.update(s => ({
58741
- ...s,
58742
- selectedValue: value,
58743
- isSubmitted: false,
58744
- error: null,
58745
- }));
58746
- this.reactionChange.emit({ value, previousValue });
58747
- }
58748
- async submitReaction() {
58749
- const currentState = this.state();
58750
- const props = this.resolvedProps();
58751
- if (!currentState.selectedValue || props.disabled)
58752
- return;
58753
- this.state.update(s => ({ ...s, isLoading: true, error: null }));
58754
- try {
58755
- // Determinar si usar endpoint anónimo o autenticado
58756
- const isAuthenticated = this.auth?.isAuthenticated() ?? false;
58757
- const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
58758
- if (useAnonymous) {
58759
- // Usar endpoint anónimo (público)
58760
- await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
58761
- }
58762
- else {
58763
- // Usar endpoint autenticado
58764
- await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
58765
- }
58766
- this.state.update(s => ({
58767
- ...s,
58768
- isLoading: false,
58769
- isSubmitted: true,
58770
- comment: '',
58771
- hadPreviousReaction: !useAnonymous, // Solo para autenticados
58772
- }));
58773
- this.reactionSubmit.emit({
58774
- value: currentState.selectedValue,
58775
- comment: currentState.comment || undefined,
58776
- entityRef: props.entityRef,
58777
- isUpdate: currentState.hadPreviousReaction,
58778
- });
58779
- if (props.showThankYou) {
58780
- this.toast.show({
58781
- message: props.thankYouMessage,
58782
- duration: 2000,
58783
- });
58784
- }
58785
- }
58786
- catch (error) {
58787
- console.error('Error submitting reaction:', error);
58788
- this.state.update(s => ({
58789
- ...s,
58790
- isLoading: false,
58791
- error: this.t('errorSubmitting'),
58792
- }));
58793
- this.toast.show({
58794
- message: this.t('errorSubmitting'),
58795
- duration: 3000,
58796
- });
58797
- }
58798
- }
58799
- updateComment(event) {
58800
- const value = event.detail.value || '';
58801
- this.state.update(s => ({ ...s, comment: value }));
58802
- }
58803
- getEmoji(index) {
58804
- return this.resolvedProps().emojis[index];
58805
- }
58806
- getEmojiLabel(index) {
58807
- return this.resolvedProps().emojiLabels[index];
58808
- }
58809
- isSelected(value) {
58810
- return this.state().selectedValue === value;
58811
- }
58812
- t(key) {
58813
- const translations = {
58814
- question: '¿Te resultó útil este contenido?',
58815
- commentPlaceholder: 'Cuéntanos más (opcional)...',
58816
- submit: 'Enviar opinión',
58817
- update: 'Actualizar opinión',
58818
- thankYou: '¡Gracias por tu opinión!',
58819
- submitted: 'Tu opinión ha sido registrada',
58820
- errorSubmitting: 'Error al enviar. Intenta de nuevo.',
58821
- positiveLabel: 'Sí, mucho',
58822
- negativeLabel: 'No tanto',
58823
- negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
58824
- };
58825
- return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
58826
- }
58827
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
58828
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
58829
- }
58830
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
58831
- type: Component,
58832
- args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
58833
- }], ctorParameters: () => [], propDecorators: { props: [{
58834
- type: Input
58835
- }], reactionSubmit: [{
58836
- type: Output
58837
- }], reactionChange: [{
58838
- type: Output
58839
- }] } });
58840
-
58841
59370
  const DEFAULT_SPLASH_SCREEN_CONFIG = {
58842
59371
  fadeOutDuration: 300,
58843
59372
  };
@@ -62690,5 +63219,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
62690
63219
  * Generated bundle index. Do not edit.
62691
63220
  */
62692
63221
 
62693
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
63222
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
62694
63223
  //# sourceMappingURL=valtech-components.mjs.map