valtech-components 2.0.1004 → 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/permissions-view/permissions-view.component.mjs +6 -9
- package/esm2022/lib/components/organisms/profile-view/profile-view.component.mjs +16 -6
- package/esm2022/lib/services/preferences/preferences.service.mjs +11 -36
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +4 -1
- package/fesm2022/valtech-components.mjs +874 -358
- 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,21 +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
|
-
// Mismo seed para theme — ThemeService aplica su valor de localStorage en
|
|
42492
|
-
// bootstrap (`currentOption`). Necesario para desgatear el effect de theme
|
|
42493
|
-
// (abajo) sin que el default 'auto' pise el tema restaurado.
|
|
42494
|
-
if (this.themeService) {
|
|
42495
|
-
this._theme.set(this.themeService.currentOption);
|
|
42496
|
-
}
|
|
42497
42492
|
// Auto-bind al user actual. effect() corre en injection context (constructor OK).
|
|
42498
42493
|
// allowSignalWrites: bindToUser/unbind escriben `_synced` (señal interna sin
|
|
42499
42494
|
// dependencias del propio effect) → seguro y necesario.
|
|
@@ -42506,18 +42501,14 @@ class PreferencesService {
|
|
|
42506
42501
|
this.unbind();
|
|
42507
42502
|
}
|
|
42508
42503
|
}, { allowSignalWrites: true });
|
|
42509
|
-
// Side-effect: aplicar theme
|
|
42510
|
-
//
|
|
42511
|
-
//
|
|
42512
|
-
//
|
|
42513
|
-
//
|
|
42514
|
-
// Firestore aún) no se aplicara: el selector mostraba "Dark" pero el tema no
|
|
42515
|
-
// cambiaba. (El selector de idioma no sufre esto porque val-language-selector
|
|
42516
|
-
// llama i18n.setLanguage directo; el de theme depende solo de este effect.)
|
|
42517
|
-
// OJO: usar `toggleUserPreference()` y NO el setter `Theme =`. El setter solo
|
|
42518
|
-
// persiste localStorage + emite el BehaviorSubject; no aplica las clases
|
|
42519
|
-
// `body.dark`/`html.ion-palette-dark` que el palette de Ionic necesita.
|
|
42504
|
+
// Side-effect: aplicar theme local cuando llega snapshot real.
|
|
42505
|
+
// OJO: usar `toggleUserPreference()` y NO el setter `Theme =`. El setter
|
|
42506
|
+
// solo persiste localStorage + emite el BehaviorSubject; no aplica las
|
|
42507
|
+
// clases `body.dark`/`body.light` que el palette `dark.class.css` de
|
|
42508
|
+
// Ionic necesita para conmutar el tema visible.
|
|
42520
42509
|
effect(() => {
|
|
42510
|
+
if (!this._synced())
|
|
42511
|
+
return;
|
|
42521
42512
|
const t = this._theme();
|
|
42522
42513
|
if (this.themeService) {
|
|
42523
42514
|
this.themeService.toggleUserPreference(t);
|
|
@@ -42532,17 +42523,11 @@ class PreferencesService {
|
|
|
42532
42523
|
this.i18n.setLanguage(l);
|
|
42533
42524
|
}
|
|
42534
42525
|
});
|
|
42535
|
-
// Side-effect: aplicar fontSize.
|
|
42536
|
-
// SIN gate `_synced` (igual que theme): el tamaño es una
|
|
42537
|
-
// preferencia local-first — FontSizeService/localStorage es la verdad del
|
|
42538
|
-
// dispositivo y `_fontSize` ya viene seedeado de ahí arriba. Aplicar siempre
|
|
42539
|
-
// que cambie permite que el cambio del usuario surta efecto al instante
|
|
42540
|
-
// (optimista, sin esperar el round-trip a Firestore) y que el sync
|
|
42541
|
-
// cross-device también aplique al llegar el snapshot. Gatearlo en `_synced`
|
|
42542
|
-
// hacía que el primer cambio de un user nuevo (sin doc Firestore aún) no se
|
|
42543
|
-
// aplicara: el selector mostraba "Grande" pero el font-size no cambiaba.
|
|
42526
|
+
// Side-effect: aplicar fontSize local cuando llega snapshot real.
|
|
42544
42527
|
// allowSignalWrites: fontSizeService.setSize() escribe this._size (signal).
|
|
42545
42528
|
effect(() => {
|
|
42529
|
+
if (!this._synced())
|
|
42530
|
+
return;
|
|
42546
42531
|
const fs = this._fontSize();
|
|
42547
42532
|
if (this.fontSizeService) {
|
|
42548
42533
|
this.fontSizeService.setSize(fs);
|
|
@@ -42718,7 +42703,7 @@ const PREFERENCES_VIEW_I18N = {
|
|
|
42718
42703
|
},
|
|
42719
42704
|
};
|
|
42720
42705
|
|
|
42721
|
-
const DEFAULT_NAMESPACE$
|
|
42706
|
+
const DEFAULT_NAMESPACE$f = 'Settings.General';
|
|
42722
42707
|
const DEFAULT_LANGUAGES = ['es', 'en'];
|
|
42723
42708
|
/**
|
|
42724
42709
|
* `val-preferences-view` — vista de preferencias de UI cross-app (tema +
|
|
@@ -42755,7 +42740,7 @@ class PreferencesViewComponent {
|
|
|
42755
42740
|
showLanguage: merged.showLanguage ?? true,
|
|
42756
42741
|
showFontSize: merged.showFontSize ?? true,
|
|
42757
42742
|
supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
|
|
42758
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
42743
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
|
|
42759
42744
|
onThemeChanged: merged.onThemeChanged,
|
|
42760
42745
|
onLanguageChanged: merged.onLanguageChanged,
|
|
42761
42746
|
onFontSizeChanged: merged.onFontSizeChanged,
|
|
@@ -44511,7 +44496,7 @@ const SECURITY_VIEW_I18N = {
|
|
|
44511
44496
|
};
|
|
44512
44497
|
|
|
44513
44498
|
addIcons({ laptopOutline, phonePortraitOutline });
|
|
44514
|
-
const DEFAULT_NAMESPACE$
|
|
44499
|
+
const DEFAULT_NAMESPACE$e = 'Settings.Security';
|
|
44515
44500
|
/**
|
|
44516
44501
|
* `val-security-view` — vista Seguridad full-feature autocontenida (organism).
|
|
44517
44502
|
*
|
|
@@ -44560,7 +44545,7 @@ class SecurityViewComponent {
|
|
|
44560
44545
|
showChangeEmail: merged.showChangeEmail ?? true,
|
|
44561
44546
|
showMfa: merged.showMfa ?? true,
|
|
44562
44547
|
showPolicies: merged.showPolicies ?? true,
|
|
44563
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
44548
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
|
|
44564
44549
|
handleDeepLinks: merged.handleDeepLinks ?? true,
|
|
44565
44550
|
homeRoute: merged.homeRoute ?? '/app/home',
|
|
44566
44551
|
onMfaCompleted: merged.onMfaCompleted,
|
|
@@ -45466,7 +45451,7 @@ const CREATE_ORG_MODAL_I18N = {
|
|
|
45466
45451
|
},
|
|
45467
45452
|
};
|
|
45468
45453
|
|
|
45469
|
-
const DEFAULT_NAMESPACE$
|
|
45454
|
+
const DEFAULT_NAMESPACE$d = 'CreateOrgModal';
|
|
45470
45455
|
/**
|
|
45471
45456
|
* `val-create-org-modal` — modal de creación de organización (organism
|
|
45472
45457
|
* compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
|
|
@@ -45492,10 +45477,10 @@ class CreateOrgModalComponent {
|
|
|
45492
45477
|
this.toast = inject(ToastService);
|
|
45493
45478
|
this.errors = inject(ValtechErrorService);
|
|
45494
45479
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
45495
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
45480
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$d;
|
|
45496
45481
|
this._busy = signal(false);
|
|
45497
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
45498
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
45482
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$d)) {
|
|
45483
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$d, CREATE_ORG_MODAL_I18N);
|
|
45499
45484
|
}
|
|
45500
45485
|
this.formMeta = signal(this.buildFormMeta());
|
|
45501
45486
|
effect(() => {
|
|
@@ -45731,7 +45716,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
|
|
|
45731
45716
|
},
|
|
45732
45717
|
};
|
|
45733
45718
|
|
|
45734
|
-
const DEFAULT_NAMESPACE$
|
|
45719
|
+
const DEFAULT_NAMESPACE$c = 'Settings.DeleteAccount';
|
|
45735
45720
|
/**
|
|
45736
45721
|
* `val-delete-account-modal` — modal de eliminación de cuenta (organism,
|
|
45737
45722
|
* cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
|
|
@@ -45757,7 +45742,7 @@ class DeleteAccountModalComponent {
|
|
|
45757
45742
|
this.i18n = inject(I18nService);
|
|
45758
45743
|
this.toast = inject(ToastService);
|
|
45759
45744
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
45760
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
45745
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$c;
|
|
45761
45746
|
this.hasPassword = signal(null);
|
|
45762
45747
|
this.codeSent = signal(false);
|
|
45763
45748
|
this.formState = signal(ComponentStates.ENABLED);
|
|
@@ -45824,8 +45809,8 @@ class DeleteAccountModalComponent {
|
|
|
45824
45809
|
],
|
|
45825
45810
|
actions: this.submitBtn('delete-code-submit', 'confirmCta'),
|
|
45826
45811
|
}));
|
|
45827
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
45828
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
45812
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
|
|
45813
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$c, DELETE_ACCOUNT_MODAL_I18N);
|
|
45829
45814
|
}
|
|
45830
45815
|
}
|
|
45831
45816
|
ngOnInit() {
|
|
@@ -46026,7 +46011,7 @@ const ORG_INFO_SHEET_I18N = {
|
|
|
46026
46011
|
},
|
|
46027
46012
|
};
|
|
46028
46013
|
|
|
46029
|
-
const DEFAULT_NAMESPACE$
|
|
46014
|
+
const DEFAULT_NAMESPACE$b = 'OrgInfoSheet';
|
|
46030
46015
|
/**
|
|
46031
46016
|
* `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
|
|
46032
46017
|
* Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
|
|
@@ -46071,15 +46056,15 @@ class OrgInfoSheetComponent {
|
|
|
46071
46056
|
text: this.closeProps.text || this.resolvedCloseLabel(),
|
|
46072
46057
|
}));
|
|
46073
46058
|
addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
|
|
46074
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
46075
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
46059
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
|
|
46060
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$b, ORG_INFO_SHEET_I18N);
|
|
46076
46061
|
}
|
|
46077
46062
|
}
|
|
46078
46063
|
dismiss() {
|
|
46079
46064
|
this._modalRef?.dismiss(null, 'cancel');
|
|
46080
46065
|
}
|
|
46081
46066
|
tt(key) {
|
|
46082
|
-
return this.i18n.t(key, DEFAULT_NAMESPACE$
|
|
46067
|
+
return this.i18n.t(key, DEFAULT_NAMESPACE$b);
|
|
46083
46068
|
}
|
|
46084
46069
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
46085
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: `
|
|
@@ -46246,7 +46231,7 @@ const SWITCH_ORG_MODAL_I18N = {
|
|
|
46246
46231
|
},
|
|
46247
46232
|
};
|
|
46248
46233
|
|
|
46249
|
-
const DEFAULT_NAMESPACE$
|
|
46234
|
+
const DEFAULT_NAMESPACE$a = 'Settings.SwitchOrg';
|
|
46250
46235
|
addIcons({ businessOutline, checkmarkCircleOutline });
|
|
46251
46236
|
/**
|
|
46252
46237
|
* `val-switch-org-modal` — modal de cambio de organización activa (organism
|
|
@@ -46272,7 +46257,7 @@ class SwitchOrgModalComponent {
|
|
|
46272
46257
|
this.orgSwitch = inject(OrgSwitchService);
|
|
46273
46258
|
this.errors = inject(ValtechErrorService);
|
|
46274
46259
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
46275
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
46260
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$a;
|
|
46276
46261
|
this.orgs = signal([]);
|
|
46277
46262
|
this.loading = signal(false);
|
|
46278
46263
|
this.query = signal('');
|
|
@@ -46288,8 +46273,8 @@ class SwitchOrgModalComponent {
|
|
|
46288
46273
|
return list;
|
|
46289
46274
|
return list.filter(o => o.name.toLowerCase().includes(q));
|
|
46290
46275
|
});
|
|
46291
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
46292
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
46276
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
|
|
46277
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$a, SWITCH_ORG_MODAL_I18N);
|
|
46293
46278
|
}
|
|
46294
46279
|
this.loadOrgs();
|
|
46295
46280
|
}
|
|
@@ -46607,7 +46592,7 @@ const ACCOUNT_VIEW_I18N = {
|
|
|
46607
46592
|
};
|
|
46608
46593
|
|
|
46609
46594
|
addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
|
|
46610
|
-
const DEFAULT_NAMESPACE$
|
|
46595
|
+
const DEFAULT_NAMESPACE$9 = 'Settings.Account';
|
|
46611
46596
|
const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
|
|
46612
46597
|
/**
|
|
46613
46598
|
* `val-account-view` — vista Cuenta full-feature autocontenida (organism).
|
|
@@ -46648,7 +46633,7 @@ class AccountViewComponent {
|
|
|
46648
46633
|
showNewOrgCta: merged.showNewOrgCta ?? true,
|
|
46649
46634
|
showLogout: merged.showLogout ?? true,
|
|
46650
46635
|
showDeleteAccount: merged.showDeleteAccount ?? true,
|
|
46651
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
46636
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$9,
|
|
46652
46637
|
manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
|
|
46653
46638
|
onOrgSwitched: merged.onOrgSwitched,
|
|
46654
46639
|
onOrgCreated: merged.onOrgCreated,
|
|
@@ -47373,7 +47358,7 @@ const EDIT_ORG_MODAL_I18N = {
|
|
|
47373
47358
|
},
|
|
47374
47359
|
};
|
|
47375
47360
|
|
|
47376
|
-
const DEFAULT_NAMESPACE$
|
|
47361
|
+
const DEFAULT_NAMESPACE$8 = 'EditOrgModal';
|
|
47377
47362
|
/**
|
|
47378
47363
|
* `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
|
|
47379
47364
|
* Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
|
|
@@ -47400,10 +47385,10 @@ class EditOrgModalComponent {
|
|
|
47400
47385
|
/** Organización a editar (pasada por `componentProps`). */
|
|
47401
47386
|
this.org = null;
|
|
47402
47387
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
47403
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
47388
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$8;
|
|
47404
47389
|
this.saving = signal(false);
|
|
47405
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
47406
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
47390
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
|
|
47391
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$8, EDIT_ORG_MODAL_I18N);
|
|
47407
47392
|
}
|
|
47408
47393
|
this.formMeta = signal(this.buildFormMeta());
|
|
47409
47394
|
effect(() => {
|
|
@@ -47641,7 +47626,7 @@ const INVITE_MEMBER_MODAL_I18N = {
|
|
|
47641
47626
|
},
|
|
47642
47627
|
};
|
|
47643
47628
|
|
|
47644
|
-
const DEFAULT_NAMESPACE$
|
|
47629
|
+
const DEFAULT_NAMESPACE$7 = 'InviteModal';
|
|
47645
47630
|
addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
|
|
47646
47631
|
/**
|
|
47647
47632
|
* `val-invite-member-modal` — invitación de miembros a una organización.
|
|
@@ -47672,7 +47657,7 @@ class InviteMemberModalComponent {
|
|
|
47672
47657
|
/** Ids de miembros ya existentes (excluidos de los resultados). */
|
|
47673
47658
|
this.existingMemberIds = [];
|
|
47674
47659
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
47675
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
47660
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$7;
|
|
47676
47661
|
this.query = signal('');
|
|
47677
47662
|
this.searching = signal(false);
|
|
47678
47663
|
this.searchResults = signal([]);
|
|
@@ -47719,8 +47704,8 @@ class InviteMemberModalComponent {
|
|
|
47719
47704
|
state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
47720
47705
|
}));
|
|
47721
47706
|
this.searchSubject = new Subject();
|
|
47722
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
47723
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
47707
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
|
|
47708
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$7, INVITE_MEMBER_MODAL_I18N);
|
|
47724
47709
|
}
|
|
47725
47710
|
this.loadRoles();
|
|
47726
47711
|
this.roleSub = this.roleControl.valueChanges.subscribe(v => {
|
|
@@ -48209,7 +48194,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
|
|
|
48209
48194
|
},
|
|
48210
48195
|
};
|
|
48211
48196
|
|
|
48212
|
-
const DEFAULT_NAMESPACE$
|
|
48197
|
+
const DEFAULT_NAMESPACE$6 = 'Settings.MemberDetail';
|
|
48213
48198
|
/**
|
|
48214
48199
|
* `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
|
|
48215
48200
|
* permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
|
|
@@ -48246,7 +48231,7 @@ class MemberDetailModalComponent {
|
|
|
48246
48231
|
{ id: 'admin', name: 'admin' },
|
|
48247
48232
|
];
|
|
48248
48233
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
48249
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
48234
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$6;
|
|
48250
48235
|
this.working = signal(false);
|
|
48251
48236
|
this.currentRole = signal('');
|
|
48252
48237
|
this.roleControl = new FormControl('');
|
|
@@ -48286,8 +48271,8 @@ class MemberDetailModalComponent {
|
|
|
48286
48271
|
type: 'button',
|
|
48287
48272
|
state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
48288
48273
|
}));
|
|
48289
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
48290
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
48274
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
|
|
48275
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$6, MEMBER_DETAIL_MODAL_I18N);
|
|
48291
48276
|
}
|
|
48292
48277
|
}
|
|
48293
48278
|
ngOnInit() {
|
|
@@ -48624,7 +48609,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
|
|
|
48624
48609
|
},
|
|
48625
48610
|
};
|
|
48626
48611
|
|
|
48627
|
-
const DEFAULT_NAMESPACE$
|
|
48612
|
+
const DEFAULT_NAMESPACE$5 = 'Settings.TransferModal';
|
|
48628
48613
|
/**
|
|
48629
48614
|
* `val-transfer-ownership-modal` — selector de miembro para transferir la
|
|
48630
48615
|
* propiedad de la organización. Promovido desde `showcase` bajo el proceso de
|
|
@@ -48648,7 +48633,7 @@ class TransferOwnershipModalComponent {
|
|
|
48648
48633
|
/** Lista de miembros transferibles (todos excepto el owner actual). */
|
|
48649
48634
|
this.members = [];
|
|
48650
48635
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
48651
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
48636
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$5;
|
|
48652
48637
|
this.query = signal('');
|
|
48653
48638
|
this.selectedId = signal('');
|
|
48654
48639
|
this.filtered = computed(() => {
|
|
@@ -48660,8 +48645,8 @@ class TransferOwnershipModalComponent {
|
|
|
48660
48645
|
m.userId.toLowerCase().includes(q));
|
|
48661
48646
|
});
|
|
48662
48647
|
this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
|
|
48663
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
48664
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
48648
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
|
|
48649
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$5, TRANSFER_OWNERSHIP_MODAL_I18N);
|
|
48665
48650
|
}
|
|
48666
48651
|
}
|
|
48667
48652
|
ngOnInit() {
|
|
@@ -48994,7 +48979,7 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
48994
48979
|
},
|
|
48995
48980
|
};
|
|
48996
48981
|
|
|
48997
|
-
const DEFAULT_NAMESPACE$
|
|
48982
|
+
const DEFAULT_NAMESPACE$4 = 'Settings.Organization';
|
|
48998
48983
|
const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
|
|
48999
48984
|
const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
|
|
49000
48985
|
/**
|
|
@@ -49051,7 +49036,7 @@ class OrganizationViewComponent {
|
|
|
49051
49036
|
showInvite: merged.showInvite ?? true,
|
|
49052
49037
|
showTransferOwnership: merged.showTransferOwnership ?? true,
|
|
49053
49038
|
showLeave: merged.showLeave ?? true,
|
|
49054
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
49039
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$4,
|
|
49055
49040
|
viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
|
|
49056
49041
|
leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
|
|
49057
49042
|
onOrgEdited: merged.onOrgEdited,
|
|
@@ -50138,7 +50123,7 @@ addIcons({
|
|
|
50138
50123
|
notificationsOutline,
|
|
50139
50124
|
refreshOutline,
|
|
50140
50125
|
});
|
|
50141
|
-
const DEFAULT_NAMESPACE$
|
|
50126
|
+
const DEFAULT_NAMESPACE$3 = 'Settings.Notifications';
|
|
50142
50127
|
/**
|
|
50143
50128
|
* `val-notification-preferences-view` — **preferencias de notificaciones push**
|
|
50144
50129
|
* (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
|
|
@@ -50181,7 +50166,7 @@ class NotificationPreferencesViewComponent {
|
|
|
50181
50166
|
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
50182
50167
|
return {
|
|
50183
50168
|
showDetails: merged.showDetails ?? true,
|
|
50184
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
50169
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
|
|
50185
50170
|
onPushEnabled: merged.onPushEnabled,
|
|
50186
50171
|
onPushDisabled: merged.onPushDisabled,
|
|
50187
50172
|
};
|
|
@@ -50952,7 +50937,7 @@ const PERMISSIONS_VIEW_I18N = {
|
|
|
50952
50937
|
},
|
|
50953
50938
|
};
|
|
50954
50939
|
|
|
50955
|
-
const DEFAULT_NAMESPACE$
|
|
50940
|
+
const DEFAULT_NAMESPACE$2 = 'Settings.Permissions';
|
|
50956
50941
|
/**
|
|
50957
50942
|
* `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
|
|
50958
50943
|
* Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
|
|
@@ -50992,7 +50977,7 @@ class PermissionsViewComponent {
|
|
|
50992
50977
|
return {
|
|
50993
50978
|
showSystemRoles: merged.showSystemRoles ?? true,
|
|
50994
50979
|
showCustomRoles: merged.showCustomRoles ?? true,
|
|
50995
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
50980
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
|
|
50996
50981
|
};
|
|
50997
50982
|
});
|
|
50998
50983
|
this.loading = signal(false);
|
|
@@ -51057,16 +51042,13 @@ class PermissionsViewComponent {
|
|
|
51057
51042
|
for (const perm of role.permissions) {
|
|
51058
51043
|
const [resource, action] = perm.split(':');
|
|
51059
51044
|
const entry = byResource.get(resource);
|
|
51060
|
-
//
|
|
51061
|
-
//
|
|
51062
|
-
|
|
51063
|
-
// Nota: el rol puede traer el permiso aunque el recurso esté filtrado del
|
|
51064
|
-
// catálogo (ej. admin tiene templates:*); por eso se descarta lo no resuelto.
|
|
51065
|
-
if (!entry || entry.scope === 'internal') {
|
|
51045
|
+
// internal (operación de plataforma) NUNCA se muestra a nivel de producto
|
|
51046
|
+
// — defensa en profundidad junto al filtro del backend (ADR-024).
|
|
51047
|
+
if (entry?.scope === 'internal') {
|
|
51066
51048
|
continue;
|
|
51067
51049
|
}
|
|
51068
51050
|
const label = this.permLabel(resource, action);
|
|
51069
|
-
if (entry
|
|
51051
|
+
if (entry?.scope === 'app') {
|
|
51070
51052
|
if (entry.appId === currentAppId) {
|
|
51071
51053
|
appPerms.push(label);
|
|
51072
51054
|
}
|
|
@@ -51078,7 +51060,7 @@ class PermissionsViewComponent {
|
|
|
51078
51060
|
}
|
|
51079
51061
|
}
|
|
51080
51062
|
else {
|
|
51081
|
-
//
|
|
51063
|
+
// org o desconocido → gestión de la organización del cliente
|
|
51082
51064
|
orgPerms.push(label);
|
|
51083
51065
|
}
|
|
51084
51066
|
}
|
|
@@ -51769,7 +51751,7 @@ addIcons({
|
|
|
51769
51751
|
openOutline,
|
|
51770
51752
|
personOutline,
|
|
51771
51753
|
});
|
|
51772
|
-
const DEFAULT_NAMESPACE = 'Notifications';
|
|
51754
|
+
const DEFAULT_NAMESPACE$1 = 'Notifications';
|
|
51773
51755
|
const DEFAULT_SETTINGS_PATH = '/app/settings/notifications';
|
|
51774
51756
|
const DEFAULT_PAGE_SIZE = 10;
|
|
51775
51757
|
const DEFAULT_MAX_LOAD = 50;
|
|
@@ -51829,7 +51811,7 @@ class NotificationsViewComponent {
|
|
|
51829
51811
|
showPushOffCta: merged.showPushOffCta ?? true,
|
|
51830
51812
|
enableMarkAll: merged.enableMarkAll ?? true,
|
|
51831
51813
|
settingsNotificationsPath: merged.settingsNotificationsPath ?? DEFAULT_SETTINGS_PATH,
|
|
51832
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
|
|
51814
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
|
|
51833
51815
|
pageSize: merged.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
51834
51816
|
maxLoad: merged.maxLoad ?? DEFAULT_MAX_LOAD,
|
|
51835
51817
|
onNotificationOpen: merged.onNotificationOpen,
|
|
@@ -52497,6 +52479,797 @@ function provideValtechNotificationsRoutes(opts) {
|
|
|
52497
52479
|
];
|
|
52498
52480
|
}
|
|
52499
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
|
+
|
|
52500
53273
|
/** Built-in label sets for the three platform locales. */
|
|
52501
53274
|
const CALLOUT_LABELS = {
|
|
52502
53275
|
es: {
|
|
@@ -58594,263 +59367,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
58594
59367
|
type: Output
|
|
58595
59368
|
}] } });
|
|
58596
59369
|
|
|
58597
|
-
/**
|
|
58598
|
-
* Defaults i18n del componente (es/en). El componente los auto-registra en su
|
|
58599
|
-
* constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
|
|
58600
|
-
* garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
|
|
58601
|
-
* template por cada change-detection → `console.warn` por CD → con el
|
|
58602
|
-
* `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
|
|
58603
|
-
* infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
|
|
58604
|
-
*/
|
|
58605
|
-
const CONTENT_REACTION_I18N = {
|
|
58606
|
-
es: {
|
|
58607
|
-
question: '¿Te resultó útil este contenido?',
|
|
58608
|
-
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
58609
|
-
submit: 'Enviar opinión',
|
|
58610
|
-
update: 'Actualizar opinión',
|
|
58611
|
-
thankYou: '¡Gracias por tu opinión!',
|
|
58612
|
-
submitted: 'Tu opinión ha sido registrada',
|
|
58613
|
-
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
58614
|
-
positiveLabel: 'Sí, mucho',
|
|
58615
|
-
negativeLabel: 'No tanto',
|
|
58616
|
-
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
58617
|
-
},
|
|
58618
|
-
en: {
|
|
58619
|
-
question: 'Was this content helpful?',
|
|
58620
|
-
commentPlaceholder: 'Tell us more (optional)...',
|
|
58621
|
-
submit: 'Send feedback',
|
|
58622
|
-
update: 'Update feedback',
|
|
58623
|
-
thankYou: 'Thanks for your feedback!',
|
|
58624
|
-
submitted: 'Your feedback has been recorded',
|
|
58625
|
-
errorSubmitting: 'Failed to send. Please try again.',
|
|
58626
|
-
positiveLabel: 'Yes, a lot',
|
|
58627
|
-
negativeLabel: 'Not really',
|
|
58628
|
-
negativeFeedbackTitle: "Tell us what you didn't like?",
|
|
58629
|
-
},
|
|
58630
|
-
};
|
|
58631
|
-
/**
|
|
58632
|
-
* Componente para reacciones de contenido con emojis.
|
|
58633
|
-
*
|
|
58634
|
-
* @example
|
|
58635
|
-
* ```html
|
|
58636
|
-
* <val-content-reaction
|
|
58637
|
-
* [props]="{
|
|
58638
|
-
* entityRef: { entityType: 'article', entityId: 'art-123' },
|
|
58639
|
-
* question: '¿Te fue útil este artículo?'
|
|
58640
|
-
* }"
|
|
58641
|
-
* (reactionSubmit)="onReactionSubmit($event)"
|
|
58642
|
-
* />
|
|
58643
|
-
* ```
|
|
58644
|
-
*/
|
|
58645
|
-
class ContentReactionComponent {
|
|
58646
|
-
constructor() {
|
|
58647
|
-
this.feedbackService = inject(FeedbackService);
|
|
58648
|
-
this.toast = inject(ToastService);
|
|
58649
|
-
this.i18n = inject(I18nService);
|
|
58650
|
-
this.auth = inject(AuthService, { optional: true });
|
|
58651
|
-
this.isConfigured = computed(() => this.feedbackService.isConfigured());
|
|
58652
|
-
this.props = {};
|
|
58653
|
-
this.reactionSubmit = new EventEmitter();
|
|
58654
|
-
this.reactionChange = new EventEmitter();
|
|
58655
|
-
// Estado reactivo
|
|
58656
|
-
this.state = signal({
|
|
58657
|
-
selectedValue: null,
|
|
58658
|
-
comment: '',
|
|
58659
|
-
isLoading: false,
|
|
58660
|
-
isSubmitted: false,
|
|
58661
|
-
hadPreviousReaction: false,
|
|
58662
|
-
error: null,
|
|
58663
|
-
});
|
|
58664
|
-
// Valores por defecto
|
|
58665
|
-
this.defaultEmojis = ['😞', '😐', '😊'];
|
|
58666
|
-
this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
|
|
58667
|
-
// Computed properties
|
|
58668
|
-
this.resolvedProps = computed(() => ({
|
|
58669
|
-
entityRef: this.props.entityRef,
|
|
58670
|
-
question: this.props.question || this.t('question'),
|
|
58671
|
-
showComment: this.props.showComment ?? true,
|
|
58672
|
-
commentOnValues: this.props.commentOnValues,
|
|
58673
|
-
commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
|
|
58674
|
-
maxCommentLength: this.props.maxCommentLength ?? 500,
|
|
58675
|
-
emojis: this.props.emojis || this.defaultEmojis,
|
|
58676
|
-
emojiLabels: this.props.emojiLabels || this.defaultLabels,
|
|
58677
|
-
showThankYou: this.props.showThankYou ?? true,
|
|
58678
|
-
thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
|
|
58679
|
-
disabled: this.props.disabled ?? false,
|
|
58680
|
-
readonly: this.props.readonly ?? false,
|
|
58681
|
-
// Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
|
|
58682
|
-
variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
|
|
58683
|
-
}));
|
|
58684
|
-
this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
|
|
58685
|
-
this.showCommentField = computed(() => {
|
|
58686
|
-
const sel = this.state().selectedValue;
|
|
58687
|
-
if (!sel)
|
|
58688
|
-
return false;
|
|
58689
|
-
const commentOn = this.resolvedProps().commentOnValues;
|
|
58690
|
-
if (commentOn !== undefined)
|
|
58691
|
-
return commentOn.includes(sel);
|
|
58692
|
-
return this.resolvedProps().showComment;
|
|
58693
|
-
});
|
|
58694
|
-
this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
|
|
58695
|
-
// Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
|
|
58696
|
-
// namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
|
|
58697
|
-
// CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
|
|
58698
|
-
if (!this.i18n.hasNamespace('ContentReaction')) {
|
|
58699
|
-
this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
|
|
58700
|
-
}
|
|
58701
|
-
}
|
|
58702
|
-
ngOnInit() {
|
|
58703
|
-
this.loadPreviousReaction();
|
|
58704
|
-
}
|
|
58705
|
-
ngOnChanges(changes) {
|
|
58706
|
-
if (changes['props'] && !changes['props'].firstChange) {
|
|
58707
|
-
this.loadPreviousReaction();
|
|
58708
|
-
}
|
|
58709
|
-
}
|
|
58710
|
-
async loadPreviousReaction() {
|
|
58711
|
-
if (!this.props.entityRef)
|
|
58712
|
-
return;
|
|
58713
|
-
// Si hay initialValue, usarlo directamente sin consultar
|
|
58714
|
-
if (this.props.initialValue) {
|
|
58715
|
-
this.state.update(s => ({
|
|
58716
|
-
...s,
|
|
58717
|
-
selectedValue: this.props.initialValue,
|
|
58718
|
-
hadPreviousReaction: true,
|
|
58719
|
-
isLoading: false,
|
|
58720
|
-
isSubmitted: true,
|
|
58721
|
-
}));
|
|
58722
|
-
return;
|
|
58723
|
-
}
|
|
58724
|
-
// Si skipInitialCheck está activo, no consultar
|
|
58725
|
-
if (this.props.skipInitialCheck) {
|
|
58726
|
-
return;
|
|
58727
|
-
}
|
|
58728
|
-
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
58729
|
-
try {
|
|
58730
|
-
const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
|
|
58731
|
-
if (check.hasFeedback && check.reactionValue) {
|
|
58732
|
-
this.state.update(s => ({
|
|
58733
|
-
...s,
|
|
58734
|
-
selectedValue: check.reactionValue,
|
|
58735
|
-
hadPreviousReaction: true,
|
|
58736
|
-
isLoading: false,
|
|
58737
|
-
isSubmitted: true,
|
|
58738
|
-
}));
|
|
58739
|
-
}
|
|
58740
|
-
else {
|
|
58741
|
-
this.state.update(s => ({ ...s, isLoading: false }));
|
|
58742
|
-
}
|
|
58743
|
-
}
|
|
58744
|
-
catch (error) {
|
|
58745
|
-
console.error('Error loading previous reaction:', error);
|
|
58746
|
-
this.state.update(s => ({ ...s, isLoading: false }));
|
|
58747
|
-
}
|
|
58748
|
-
}
|
|
58749
|
-
selectReaction(value) {
|
|
58750
|
-
if (this.resolvedProps().disabled || this.resolvedProps().readonly)
|
|
58751
|
-
return;
|
|
58752
|
-
const previousValue = this.state().selectedValue;
|
|
58753
|
-
this.state.update(s => ({
|
|
58754
|
-
...s,
|
|
58755
|
-
selectedValue: value,
|
|
58756
|
-
isSubmitted: false,
|
|
58757
|
-
error: null,
|
|
58758
|
-
}));
|
|
58759
|
-
this.reactionChange.emit({ value, previousValue });
|
|
58760
|
-
}
|
|
58761
|
-
async submitReaction() {
|
|
58762
|
-
const currentState = this.state();
|
|
58763
|
-
const props = this.resolvedProps();
|
|
58764
|
-
if (!currentState.selectedValue || props.disabled)
|
|
58765
|
-
return;
|
|
58766
|
-
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
58767
|
-
try {
|
|
58768
|
-
// Determinar si usar endpoint anónimo o autenticado
|
|
58769
|
-
const isAuthenticated = this.auth?.isAuthenticated() ?? false;
|
|
58770
|
-
const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
|
|
58771
|
-
if (useAnonymous) {
|
|
58772
|
-
// Usar endpoint anónimo (público)
|
|
58773
|
-
await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
58774
|
-
}
|
|
58775
|
-
else {
|
|
58776
|
-
// Usar endpoint autenticado
|
|
58777
|
-
await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
58778
|
-
}
|
|
58779
|
-
this.state.update(s => ({
|
|
58780
|
-
...s,
|
|
58781
|
-
isLoading: false,
|
|
58782
|
-
isSubmitted: true,
|
|
58783
|
-
comment: '',
|
|
58784
|
-
hadPreviousReaction: !useAnonymous, // Solo para autenticados
|
|
58785
|
-
}));
|
|
58786
|
-
this.reactionSubmit.emit({
|
|
58787
|
-
value: currentState.selectedValue,
|
|
58788
|
-
comment: currentState.comment || undefined,
|
|
58789
|
-
entityRef: props.entityRef,
|
|
58790
|
-
isUpdate: currentState.hadPreviousReaction,
|
|
58791
|
-
});
|
|
58792
|
-
if (props.showThankYou) {
|
|
58793
|
-
this.toast.show({
|
|
58794
|
-
message: props.thankYouMessage,
|
|
58795
|
-
duration: 2000,
|
|
58796
|
-
});
|
|
58797
|
-
}
|
|
58798
|
-
}
|
|
58799
|
-
catch (error) {
|
|
58800
|
-
console.error('Error submitting reaction:', error);
|
|
58801
|
-
this.state.update(s => ({
|
|
58802
|
-
...s,
|
|
58803
|
-
isLoading: false,
|
|
58804
|
-
error: this.t('errorSubmitting'),
|
|
58805
|
-
}));
|
|
58806
|
-
this.toast.show({
|
|
58807
|
-
message: this.t('errorSubmitting'),
|
|
58808
|
-
duration: 3000,
|
|
58809
|
-
});
|
|
58810
|
-
}
|
|
58811
|
-
}
|
|
58812
|
-
updateComment(event) {
|
|
58813
|
-
const value = event.detail.value || '';
|
|
58814
|
-
this.state.update(s => ({ ...s, comment: value }));
|
|
58815
|
-
}
|
|
58816
|
-
getEmoji(index) {
|
|
58817
|
-
return this.resolvedProps().emojis[index];
|
|
58818
|
-
}
|
|
58819
|
-
getEmojiLabel(index) {
|
|
58820
|
-
return this.resolvedProps().emojiLabels[index];
|
|
58821
|
-
}
|
|
58822
|
-
isSelected(value) {
|
|
58823
|
-
return this.state().selectedValue === value;
|
|
58824
|
-
}
|
|
58825
|
-
t(key) {
|
|
58826
|
-
const translations = {
|
|
58827
|
-
question: '¿Te resultó útil este contenido?',
|
|
58828
|
-
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
58829
|
-
submit: 'Enviar opinión',
|
|
58830
|
-
update: 'Actualizar opinión',
|
|
58831
|
-
thankYou: '¡Gracias por tu opinión!',
|
|
58832
|
-
submitted: 'Tu opinión ha sido registrada',
|
|
58833
|
-
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
58834
|
-
positiveLabel: 'Sí, mucho',
|
|
58835
|
-
negativeLabel: 'No tanto',
|
|
58836
|
-
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
58837
|
-
};
|
|
58838
|
-
return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
|
|
58839
|
-
}
|
|
58840
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
58841
|
-
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"] }] }); }
|
|
58842
|
-
}
|
|
58843
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
|
|
58844
|
-
type: Component,
|
|
58845
|
-
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"] }]
|
|
58846
|
-
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
58847
|
-
type: Input
|
|
58848
|
-
}], reactionSubmit: [{
|
|
58849
|
-
type: Output
|
|
58850
|
-
}], reactionChange: [{
|
|
58851
|
-
type: Output
|
|
58852
|
-
}] } });
|
|
58853
|
-
|
|
58854
59370
|
const DEFAULT_SPLASH_SCREEN_CONFIG = {
|
|
58855
59371
|
fadeOutDuration: 300,
|
|
58856
59372
|
};
|
|
@@ -62703,5 +63219,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
62703
63219
|
* Generated bundle index. Do not edit.
|
|
62704
63220
|
*/
|
|
62705
63221
|
|
|
62706
|
-
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 };
|
|
62707
63223
|
//# sourceMappingURL=valtech-components.mjs.map
|