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.
- package/esm2022/lib/components/molecules/comment/comment.component.mjs +3 -3
- package/esm2022/lib/components/organisms/about-view/about-view.component.mjs +436 -0
- package/esm2022/lib/components/organisms/about-view/about-view.i18n.mjs +61 -0
- package/esm2022/lib/components/organisms/about-view/about.routes.mjs +36 -0
- package/esm2022/lib/components/organisms/about-view/types.mjs +19 -0
- package/esm2022/lib/components/organisms/profile-view/profile-view.component.mjs +16 -6
- package/esm2022/lib/services/preferences/preferences.service.mjs +4 -19
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +4 -1
- package/fesm2022/valtech-components.mjs +862 -333
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/about-view/about-view.component.d.ts +88 -0
- package/lib/components/organisms/about-view/about-view.i18n.d.ts +14 -0
- package/lib/components/organisms/about-view/about.routes.d.ts +31 -0
- package/lib/components/organisms/about-view/types.d.ts +76 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +3 -0
|
@@ -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.
|
|
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: ["
|
|
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$
|
|
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$
|
|
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
|
-
|
|
42267
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42348
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
45480
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$d;
|
|
45486
45481
|
this._busy = signal(false);
|
|
45487
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
45488
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
45818
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
46065
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
46282
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
47388
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$8;
|
|
47394
47389
|
this.saving = signal(false);
|
|
47395
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
47396
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
47713
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
48280
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
48654
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|