valtech-components 2.0.1007 → 2.0.1008
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/organization-view/organization-view.component.mjs +3 -141
- package/esm2022/lib/components/organisms/organization-view/organization-view.i18n.mjs +1 -21
- package/esm2022/lib/components/organisms/profile-view/profile-view.component.mjs +6 -16
- package/esm2022/lib/components/templates/auth-background/auth-background.component.mjs +19 -17
- package/esm2022/lib/services/auth/auth.service.mjs +1 -14
- package/esm2022/lib/services/auth/types.mjs +1 -1
- package/esm2022/lib/services/org/org.service.mjs +1 -8
- package/esm2022/lib/services/org/types.mjs +1 -1
- package/esm2022/lib/services/preferences/preferences.service.mjs +36 -11
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +1 -7
- package/fesm2022/valtech-components.mjs +370 -1905
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/organization-view/organization-view.component.d.ts +0 -6
- package/lib/components/templates/auth-background/auth-background.component.d.ts +2 -2
- package/lib/services/auth/auth.service.d.ts +1 -7
- package/lib/services/auth/types.d.ts +0 -23
- package/lib/services/org/org.service.d.ts +1 -6
- package/lib/services/org/types.d.ts +0 -30
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +1 -8
- package/esm2022/lib/components/organisms/about-view/about-view.component.mjs +0 -436
- package/esm2022/lib/components/organisms/about-view/about-view.i18n.mjs +0 -61
- package/esm2022/lib/components/organisms/about-view/about.routes.mjs +0 -36
- package/esm2022/lib/components/organisms/about-view/types.mjs +0 -19
- package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.component.mjs +0 -385
- package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.mjs +0 -63
- package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.component.mjs +0 -313
- package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.i18n.mjs +0 -63
- package/esm2022/lib/services/apikeys/api-keys.service.mjs +0 -46
- package/esm2022/lib/services/apikeys/types.mjs +0 -7
- package/lib/components/organisms/about-view/about-view.component.d.ts +0 -88
- package/lib/components/organisms/about-view/about-view.i18n.d.ts +0 -14
- package/lib/components/organisms/about-view/about.routes.d.ts +0 -31
- package/lib/components/organisms/about-view/types.d.ts +0 -76
- package/lib/components/organisms/api-keys-modal/api-keys-modal.component.d.ts +0 -44
- package/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.d.ts +0 -6
- package/lib/components/organisms/member-import-modal/member-import-modal.component.d.ts +0 -47
- package/lib/components/organisms/member-import-modal/member-import-modal.i18n.d.ts +0 -6
- package/lib/services/apikeys/api-keys.service.d.ts +0 -25
- package/lib/services/apikeys/types.d.ts +0 -46
|
@@ -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,
|
|
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';
|
|
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.1008';
|
|
58
58
|
|
|
59
59
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
60
60
|
let isRefreshing = false;
|
|
@@ -8658,19 +8658,6 @@ class AuthService {
|
|
|
8658
8658
|
.post(`${this.baseUrl}/reset-password`, request)
|
|
8659
8659
|
.pipe(catchError(error => this.handleAuthError(error)));
|
|
8660
8660
|
}
|
|
8661
|
-
/**
|
|
8662
|
-
* Activa una cuenta pre-aprovisionada por una organización (ADR-023): define
|
|
8663
|
-
* la contraseña con el token del enlace de email. En éxito el backend devuelve
|
|
8664
|
-
* tokens de auto-signin → el usuario queda logueado (mismo flujo que verify-email).
|
|
8665
|
-
*/
|
|
8666
|
-
activateAccount(request) {
|
|
8667
|
-
this.stateService.clearError();
|
|
8668
|
-
return this.http.post(`${this.baseUrl}/activate`, request).pipe(tap(response => {
|
|
8669
|
-
if (response.activated && response.accessToken) {
|
|
8670
|
-
this.handleSuccessfulAuth(response);
|
|
8671
|
-
}
|
|
8672
|
-
}), catchError(error => this.handleAuthError(error)));
|
|
8673
|
-
}
|
|
8674
8661
|
/**
|
|
8675
8662
|
* Cambia la contraseña del usuario autenticado.
|
|
8676
8663
|
* Requiere la contraseña actual para verificación.
|
|
@@ -21670,7 +21657,7 @@ class CommentComponent {
|
|
|
21670
21657
|
</div>
|
|
21671
21658
|
}
|
|
21672
21659
|
</div>
|
|
21673
|
-
`, 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"] }] }); }
|
|
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"] }] }); }
|
|
21674
21661
|
}
|
|
21675
21662
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CommentComponent, decorators: [{
|
|
21676
21663
|
type: Component,
|
|
@@ -21860,7 +21847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
21860
21847
|
</div>
|
|
21861
21848
|
}
|
|
21862
21849
|
</div>
|
|
21863
|
-
`, 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(
|
|
21850
|
+
`, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}:host{display:block}.comment{--indent-size: 24px;--max-indent: 72px;padding:12px 0}.comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.08);border-radius:8px;padding:12px;margin:-4px}.comment.pinned{border-left:3px solid var(--ion-color-primary);padding-left:12px}.comment.collapsed{opacity:.7}.pinned-indicator{display:flex;align-items:center;gap:4px;font-size:.75rem;color:var(--ion-color-primary);margin-bottom:8px;padding-left:44px}.pinned-indicator ion-icon{font-size:.875rem}.comment-main{display:flex;gap:12px}.comment-avatar{flex-shrink:0;cursor:pointer}.comment-avatar val-avatar{--size: 36px}.comment-avatar .avatar-placeholder{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--ion-color-primary),var(--ion-color-secondary));display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;font-weight:600}.comment-body{flex:1;min-width:0}.comment-header{display:flex;align-items:center;flex-wrap:wrap;gap:6px;margin-bottom:4px}.author-info{display:flex;align-items:center;gap:4px;cursor:pointer}.author-info:hover .author-name{text-decoration:underline}.author-name{font-weight:600;font-size:.875rem;color:var(--ion-text-color)}.verified-badge{color:var(--ion-color-primary);font-size:1rem}.author-username{font-size:.8125rem;color:var(--ion-color-medium)}.comment-meta{display:flex;align-items:center;gap:6px;margin-left:auto}.timestamp{font-size:.75rem;color:var(--ion-color-medium)}.edited-label{font-size:.6875rem;color:var(--ion-color-medium);font-style:italic}.more-button{--padding-start: 4px;--padding-end: 4px;margin:0;height:28px}.more-button ion-icon{font-size:1.125rem;color:var(--ion-color-medium)}.menu-divider{height:1px;background-color:var(--ion-color-light);margin:4px 0}.comment-content{font-size:.875rem;line-height:1.5;color:var(--ion-text-color);word-wrap:break-word;overflow-wrap:break-word}.comment-content :global(.comment-link){color:var(--ion-color-primary);text-decoration:none}.comment-content :global(.comment-link):hover{text-decoration:underline}.comment-content :global(.comment-mention){color:var(--ion-color-primary);font-weight:500;cursor:pointer}.comment-content :global(.comment-mention):hover{text-decoration:underline}.comment-content :global(.comment-hashtag){color:var(--ion-color-primary);cursor:pointer}.comment-content :global(.comment-hashtag):hover{text-decoration:underline}.comment-reactions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.reaction-button{display:flex;align-items:center;gap:4px;background:none;border:none;padding:4px 8px;border-radius:16px;cursor:pointer;font-size:.8125rem;color:var(--reaction-color, var(--ion-color-medium));transition:all .2s ease;background-color:transparent}.reaction-button:hover{background-color:rgba(var(--ion-color-medium-rgb),.1)}.reaction-button.active{background-color:rgba(var(--ion-color-primary-rgb),.1)}.reaction-button.active ion-icon{transform:scale(1.1)}.reaction-button ion-icon{font-size:1.125rem;transition:transform .2s ease}.reaction-button .reaction-count{font-weight:500}.comment-actions{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;margin-left:-8px}.comment-actions ion-button{--padding-start: 8px;--padding-end: 8px;font-size:.8125rem;height:32px;text-transform:none;font-weight:500}.expand-button,.collapse-button,.load-more-button,.view-replies-button{display:flex;align-items:center;gap:6px;background:none;border:none;padding:8px 12px;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);border-radius:8px;transition:background-color .2s}.expand-button:hover,.collapse-button:hover,.load-more-button:hover,.view-replies-button:hover{background-color:rgba(var(--ion-color-primary-rgb),.1)}.expand-button ion-icon,.collapse-button ion-icon,.load-more-button ion-icon,.view-replies-button ion-icon{font-size:1rem}.expand-button{margin-top:4px;color:var(--ion-color-medium)}.comment-children{margin-top:12px;padding-left:min(var(--indent-size),var(--max-indent));border-left:2px solid var(--ion-color-light-shade)}.comment-children val-comment{display:block}.comment-children val-comment:first-of-type .comment{padding-top:0}.load-more-button,.view-replies-button{margin-top:8px;margin-left:0}ion-popover{--width: 200px}ion-popover ion-list{padding:4px 0}ion-popover ion-item{--min-height: 40px;--padding-start: 12px;font-size:.875rem}ion-popover ion-item ion-icon{font-size:1.125rem;margin-right:8px}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 2px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 2px);height:max(28px,36px - 2px);font-size:max(.6875rem,.8125rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 4px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 4px);height:max(28px,36px - 4px);font-size:max(.6875rem,.75rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 6px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 6px);height:max(28px,36px - 6px);font-size:max(.6875rem,.6875rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 8px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 8px);height:max(28px,36px - 8px);font-size:max(.6875rem,.625rem)}:host-context(val-comment) .comment-avatar val-avatar{--size: max(28px, 36px - 10px)}:host-context(val-comment) .comment-avatar .avatar-placeholder{width:max(28px,36px - 10px);height:max(28px,36px - 10px);font-size:max(.6875rem,.5625rem)}:host-context(.dark) .comment.highlighted,:host-context(.ion-palette-dark) .comment.highlighted,:host-context(html.ion-palette-dark) .comment.highlighted,:host-context(body.dark) .comment.highlighted,:host-context([data-theme=dark]) .comment.highlighted{background-color:rgba(var(--ion-color-primary-rgb),.12)}:host-context(.dark) .avatar-placeholder,:host-context(.ion-palette-dark) .avatar-placeholder,:host-context(html.ion-palette-dark) .avatar-placeholder,:host-context(body.dark) .avatar-placeholder,:host-context([data-theme=dark]) .avatar-placeholder{background:linear-gradient(135deg,var(--ion-color-primary-shade),var(--ion-color-secondary-shade))}\n"] }]
|
|
21864
21851
|
}], propDecorators: { props: [{
|
|
21865
21852
|
type: Input
|
|
21866
21853
|
}], authorClick: [{
|
|
@@ -41750,7 +41737,7 @@ const PROFILE_VIEW_I18N = {
|
|
|
41750
41737
|
const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
|
|
41751
41738
|
const HANDLE_MIN = 3;
|
|
41752
41739
|
const HANDLE_MAX = 20;
|
|
41753
|
-
const DEFAULT_NAMESPACE$
|
|
41740
|
+
const DEFAULT_NAMESPACE$f = 'Settings.Profile';
|
|
41754
41741
|
/**
|
|
41755
41742
|
* `val-profile-view` — vista Perfil full-feature autocontenida (organism).
|
|
41756
41743
|
*
|
|
@@ -41800,7 +41787,7 @@ class ProfileViewComponent {
|
|
|
41800
41787
|
showPhone: merged.showPhone ?? true,
|
|
41801
41788
|
showShareCta: merged.showShareCta ?? true,
|
|
41802
41789
|
profileBaseUrl: merged.profileBaseUrl ?? '',
|
|
41803
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
41790
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
|
|
41804
41791
|
onSaved: merged.onSaved,
|
|
41805
41792
|
onAvatarUploaded: merged.onAvatarUploaded,
|
|
41806
41793
|
};
|
|
@@ -42276,17 +42263,12 @@ class ProfileViewComponent {
|
|
|
42276
42263
|
|
|
42277
42264
|
<val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
|
|
42278
42265
|
|
|
42279
|
-
|
|
42280
|
-
|
|
42281
|
-
<val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
|
|
42282
|
-
</section>
|
|
42283
|
-
}
|
|
42266
|
+
<!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
|
|
42267
|
+
Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
|
|
42284
42268
|
}
|
|
42285
42269
|
}
|
|
42286
42270
|
</div>
|
|
42287
|
-
|
|
42288
|
-
<val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
|
|
42289
|
-
`, 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"] }] }); }
|
|
42271
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.page-header h1{font-size:22px;font-weight:700;margin:0 0 4px}.hint{font-size:13px;opacity:.7;margin:0 0 12px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.avatar-section{display:flex;align-items:center;gap:16px}.avatar-meta{display:flex;flex-direction:column;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: AvatarUploadComponent, selector: "val-avatar-upload", inputs: ["props"], outputs: ["uploaded", "error", "uploadStart"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }] }); }
|
|
42290
42272
|
}
|
|
42291
42273
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ProfileViewComponent, decorators: [{
|
|
42292
42274
|
type: Component,
|
|
@@ -42362,16 +42344,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
42362
42344
|
|
|
42363
42345
|
<val-form #form [props]="formMeta" (onSubmit)="onSubmit($event)" />
|
|
42364
42346
|
|
|
42365
|
-
|
|
42366
|
-
|
|
42367
|
-
<val-cta-card [props]="shareCta()" (onAction)="shareOpen.set(true)" />
|
|
42368
|
-
</section>
|
|
42369
|
-
}
|
|
42347
|
+
<!-- Card de compartir + modal QR removidos temporalmente (se retoman luego).
|
|
42348
|
+
Wiring intacto: shareCta(), shareModal(), shareOpen, showShareCta. -->
|
|
42370
42349
|
}
|
|
42371
42350
|
}
|
|
42372
42351
|
</div>
|
|
42373
|
-
|
|
42374
|
-
<val-share-profile-modal [isOpen]="shareOpen()" [props]="shareModal()" (dismissed)="shareOpen.set(false)" />
|
|
42375
42352
|
`, 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"] }]
|
|
42376
42353
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
42377
42354
|
type: Input
|
|
@@ -42502,6 +42479,21 @@ class PreferencesService {
|
|
|
42502
42479
|
this._updateInFlight = false;
|
|
42503
42480
|
const destroyRef = inject(DestroyRef);
|
|
42504
42481
|
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
|
+
}
|
|
42505
42497
|
// Auto-bind al user actual. effect() corre en injection context (constructor OK).
|
|
42506
42498
|
// allowSignalWrites: bindToUser/unbind escriben `_synced` (señal interna sin
|
|
42507
42499
|
// dependencias del propio effect) → seguro y necesario.
|
|
@@ -42514,14 +42506,18 @@ class PreferencesService {
|
|
|
42514
42506
|
this.unbind();
|
|
42515
42507
|
}
|
|
42516
42508
|
}, { allowSignalWrites: true });
|
|
42517
|
-
// Side-effect: aplicar theme
|
|
42518
|
-
//
|
|
42519
|
-
//
|
|
42520
|
-
//
|
|
42521
|
-
//
|
|
42509
|
+
// Side-effect: aplicar theme. SIN gate `_synced` (local-first, igual que
|
|
42510
|
+
// fontSize): ThemeService/localStorage es la verdad del dispositivo y
|
|
42511
|
+
// `_theme` viene seedeado arriba. Aplicar siempre que cambie da apply
|
|
42512
|
+
// optimista al instante; el sync cross-device también aplica vía snapshot.
|
|
42513
|
+
// Gatearlo en `_synced` hacía que el primer cambio de un user nuevo (sin doc
|
|
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.
|
|
42522
42520
|
effect(() => {
|
|
42523
|
-
if (!this._synced())
|
|
42524
|
-
return;
|
|
42525
42521
|
const t = this._theme();
|
|
42526
42522
|
if (this.themeService) {
|
|
42527
42523
|
this.themeService.toggleUserPreference(t);
|
|
@@ -42536,11 +42532,17 @@ class PreferencesService {
|
|
|
42536
42532
|
this.i18n.setLanguage(l);
|
|
42537
42533
|
}
|
|
42538
42534
|
});
|
|
42539
|
-
// Side-effect: aplicar fontSize
|
|
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.
|
|
42540
42544
|
// allowSignalWrites: fontSizeService.setSize() escribe this._size (signal).
|
|
42541
42545
|
effect(() => {
|
|
42542
|
-
if (!this._synced())
|
|
42543
|
-
return;
|
|
42544
42546
|
const fs = this._fontSize();
|
|
42545
42547
|
if (this.fontSizeService) {
|
|
42546
42548
|
this.fontSizeService.setSize(fs);
|
|
@@ -42716,7 +42718,7 @@ const PREFERENCES_VIEW_I18N = {
|
|
|
42716
42718
|
},
|
|
42717
42719
|
};
|
|
42718
42720
|
|
|
42719
|
-
const DEFAULT_NAMESPACE$
|
|
42721
|
+
const DEFAULT_NAMESPACE$e = 'Settings.General';
|
|
42720
42722
|
const DEFAULT_LANGUAGES = ['es', 'en'];
|
|
42721
42723
|
/**
|
|
42722
42724
|
* `val-preferences-view` — vista de preferencias de UI cross-app (tema +
|
|
@@ -42753,7 +42755,7 @@ class PreferencesViewComponent {
|
|
|
42753
42755
|
showLanguage: merged.showLanguage ?? true,
|
|
42754
42756
|
showFontSize: merged.showFontSize ?? true,
|
|
42755
42757
|
supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
|
|
42756
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
42758
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
|
|
42757
42759
|
onThemeChanged: merged.onThemeChanged,
|
|
42758
42760
|
onLanguageChanged: merged.onLanguageChanged,
|
|
42759
42761
|
onFontSizeChanged: merged.onFontSizeChanged,
|
|
@@ -44509,7 +44511,7 @@ const SECURITY_VIEW_I18N = {
|
|
|
44509
44511
|
};
|
|
44510
44512
|
|
|
44511
44513
|
addIcons({ laptopOutline, phonePortraitOutline });
|
|
44512
|
-
const DEFAULT_NAMESPACE$
|
|
44514
|
+
const DEFAULT_NAMESPACE$d = 'Settings.Security';
|
|
44513
44515
|
/**
|
|
44514
44516
|
* `val-security-view` — vista Seguridad full-feature autocontenida (organism).
|
|
44515
44517
|
*
|
|
@@ -44558,7 +44560,7 @@ class SecurityViewComponent {
|
|
|
44558
44560
|
showChangeEmail: merged.showChangeEmail ?? true,
|
|
44559
44561
|
showMfa: merged.showMfa ?? true,
|
|
44560
44562
|
showPolicies: merged.showPolicies ?? true,
|
|
44561
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
44563
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$d,
|
|
44562
44564
|
handleDeepLinks: merged.handleDeepLinks ?? true,
|
|
44563
44565
|
homeRoute: merged.homeRoute ?? '/app/home',
|
|
44564
44566
|
onMfaCompleted: merged.onMfaCompleted,
|
|
@@ -45360,13 +45362,6 @@ class OrgService {
|
|
|
45360
45362
|
inviteUser(orgId, req) {
|
|
45361
45363
|
return this.http.post(`${this.baseUrl}/${orgId}/invite`, req);
|
|
45362
45364
|
}
|
|
45363
|
-
/**
|
|
45364
|
-
* Crea/asigna miembros en masa (ADR-023). Resultado por-fila (partial success):
|
|
45365
|
-
* `created` (pre-aprovisionado), `assigned` (ya existía), `skipped`, `error`.
|
|
45366
|
-
*/
|
|
45367
|
-
importMembers(orgId, req) {
|
|
45368
|
-
return this.http.post(`${this.baseUrl}/${orgId}/members/import`, req);
|
|
45369
|
-
}
|
|
45370
45365
|
leaveOrg(orgId) {
|
|
45371
45366
|
return this.http.post(`${this.baseUrl}/${orgId}/leave`, {});
|
|
45372
45367
|
}
|
|
@@ -45471,7 +45466,7 @@ const CREATE_ORG_MODAL_I18N = {
|
|
|
45471
45466
|
},
|
|
45472
45467
|
};
|
|
45473
45468
|
|
|
45474
|
-
const DEFAULT_NAMESPACE$
|
|
45469
|
+
const DEFAULT_NAMESPACE$c = 'CreateOrgModal';
|
|
45475
45470
|
/**
|
|
45476
45471
|
* `val-create-org-modal` — modal de creación de organización (organism
|
|
45477
45472
|
* compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
|
|
@@ -45497,10 +45492,10 @@ class CreateOrgModalComponent {
|
|
|
45497
45492
|
this.toast = inject(ToastService);
|
|
45498
45493
|
this.errors = inject(ValtechErrorService);
|
|
45499
45494
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
45500
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
45495
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$c;
|
|
45501
45496
|
this._busy = signal(false);
|
|
45502
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
45503
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
45497
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
|
|
45498
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$c, CREATE_ORG_MODAL_I18N);
|
|
45504
45499
|
}
|
|
45505
45500
|
this.formMeta = signal(this.buildFormMeta());
|
|
45506
45501
|
effect(() => {
|
|
@@ -45736,7 +45731,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
|
|
|
45736
45731
|
},
|
|
45737
45732
|
};
|
|
45738
45733
|
|
|
45739
|
-
const DEFAULT_NAMESPACE$
|
|
45734
|
+
const DEFAULT_NAMESPACE$b = 'Settings.DeleteAccount';
|
|
45740
45735
|
/**
|
|
45741
45736
|
* `val-delete-account-modal` — modal de eliminación de cuenta (organism,
|
|
45742
45737
|
* cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
|
|
@@ -45762,7 +45757,7 @@ class DeleteAccountModalComponent {
|
|
|
45762
45757
|
this.i18n = inject(I18nService);
|
|
45763
45758
|
this.toast = inject(ToastService);
|
|
45764
45759
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
45765
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
45760
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$b;
|
|
45766
45761
|
this.hasPassword = signal(null);
|
|
45767
45762
|
this.codeSent = signal(false);
|
|
45768
45763
|
this.formState = signal(ComponentStates.ENABLED);
|
|
@@ -45829,8 +45824,8 @@ class DeleteAccountModalComponent {
|
|
|
45829
45824
|
],
|
|
45830
45825
|
actions: this.submitBtn('delete-code-submit', 'confirmCta'),
|
|
45831
45826
|
}));
|
|
45832
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
45833
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
45827
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
|
|
45828
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$b, DELETE_ACCOUNT_MODAL_I18N);
|
|
45834
45829
|
}
|
|
45835
45830
|
}
|
|
45836
45831
|
ngOnInit() {
|
|
@@ -46031,7 +46026,7 @@ const ORG_INFO_SHEET_I18N = {
|
|
|
46031
46026
|
},
|
|
46032
46027
|
};
|
|
46033
46028
|
|
|
46034
|
-
const DEFAULT_NAMESPACE$
|
|
46029
|
+
const DEFAULT_NAMESPACE$a = 'OrgInfoSheet';
|
|
46035
46030
|
/**
|
|
46036
46031
|
* `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
|
|
46037
46032
|
* Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
|
|
@@ -46076,15 +46071,15 @@ class OrgInfoSheetComponent {
|
|
|
46076
46071
|
text: this.closeProps.text || this.resolvedCloseLabel(),
|
|
46077
46072
|
}));
|
|
46078
46073
|
addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
|
|
46079
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
46080
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
46074
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
|
|
46075
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$a, ORG_INFO_SHEET_I18N);
|
|
46081
46076
|
}
|
|
46082
46077
|
}
|
|
46083
46078
|
dismiss() {
|
|
46084
46079
|
this._modalRef?.dismiss(null, 'cancel');
|
|
46085
46080
|
}
|
|
46086
46081
|
tt(key) {
|
|
46087
|
-
return this.i18n.t(key, DEFAULT_NAMESPACE$
|
|
46082
|
+
return this.i18n.t(key, DEFAULT_NAMESPACE$a);
|
|
46088
46083
|
}
|
|
46089
46084
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
46090
46085
|
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: `
|
|
@@ -46251,7 +46246,7 @@ const SWITCH_ORG_MODAL_I18N = {
|
|
|
46251
46246
|
},
|
|
46252
46247
|
};
|
|
46253
46248
|
|
|
46254
|
-
const DEFAULT_NAMESPACE$
|
|
46249
|
+
const DEFAULT_NAMESPACE$9 = 'Settings.SwitchOrg';
|
|
46255
46250
|
addIcons({ businessOutline, checkmarkCircleOutline });
|
|
46256
46251
|
/**
|
|
46257
46252
|
* `val-switch-org-modal` — modal de cambio de organización activa (organism
|
|
@@ -46277,7 +46272,7 @@ class SwitchOrgModalComponent {
|
|
|
46277
46272
|
this.orgSwitch = inject(OrgSwitchService);
|
|
46278
46273
|
this.errors = inject(ValtechErrorService);
|
|
46279
46274
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
46280
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
46275
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$9;
|
|
46281
46276
|
this.orgs = signal([]);
|
|
46282
46277
|
this.loading = signal(false);
|
|
46283
46278
|
this.query = signal('');
|
|
@@ -46293,8 +46288,8 @@ class SwitchOrgModalComponent {
|
|
|
46293
46288
|
return list;
|
|
46294
46289
|
return list.filter(o => o.name.toLowerCase().includes(q));
|
|
46295
46290
|
});
|
|
46296
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
46297
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
46291
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
|
|
46292
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$9, SWITCH_ORG_MODAL_I18N);
|
|
46298
46293
|
}
|
|
46299
46294
|
this.loadOrgs();
|
|
46300
46295
|
}
|
|
@@ -46612,7 +46607,7 @@ const ACCOUNT_VIEW_I18N = {
|
|
|
46612
46607
|
};
|
|
46613
46608
|
|
|
46614
46609
|
addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
|
|
46615
|
-
const DEFAULT_NAMESPACE$
|
|
46610
|
+
const DEFAULT_NAMESPACE$8 = 'Settings.Account';
|
|
46616
46611
|
const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
|
|
46617
46612
|
/**
|
|
46618
46613
|
* `val-account-view` — vista Cuenta full-feature autocontenida (organism).
|
|
@@ -46653,7 +46648,7 @@ class AccountViewComponent {
|
|
|
46653
46648
|
showNewOrgCta: merged.showNewOrgCta ?? true,
|
|
46654
46649
|
showLogout: merged.showLogout ?? true,
|
|
46655
46650
|
showDeleteAccount: merged.showDeleteAccount ?? true,
|
|
46656
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
46651
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$8,
|
|
46657
46652
|
manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
|
|
46658
46653
|
onOrgSwitched: merged.onOrgSwitched,
|
|
46659
46654
|
onOrgCreated: merged.onOrgCreated,
|
|
@@ -47378,7 +47373,7 @@ const EDIT_ORG_MODAL_I18N = {
|
|
|
47378
47373
|
},
|
|
47379
47374
|
};
|
|
47380
47375
|
|
|
47381
|
-
const DEFAULT_NAMESPACE$
|
|
47376
|
+
const DEFAULT_NAMESPACE$7 = 'EditOrgModal';
|
|
47382
47377
|
/**
|
|
47383
47378
|
* `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
|
|
47384
47379
|
* Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
|
|
@@ -47405,10 +47400,10 @@ class EditOrgModalComponent {
|
|
|
47405
47400
|
/** Organización a editar (pasada por `componentProps`). */
|
|
47406
47401
|
this.org = null;
|
|
47407
47402
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
47408
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
47403
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$7;
|
|
47409
47404
|
this.saving = signal(false);
|
|
47410
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
47411
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
47405
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
|
|
47406
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$7, EDIT_ORG_MODAL_I18N);
|
|
47412
47407
|
}
|
|
47413
47408
|
this.formMeta = signal(this.buildFormMeta());
|
|
47414
47409
|
effect(() => {
|
|
@@ -47646,7 +47641,7 @@ const INVITE_MEMBER_MODAL_I18N = {
|
|
|
47646
47641
|
},
|
|
47647
47642
|
};
|
|
47648
47643
|
|
|
47649
|
-
const DEFAULT_NAMESPACE$
|
|
47644
|
+
const DEFAULT_NAMESPACE$6 = 'InviteModal';
|
|
47650
47645
|
addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
|
|
47651
47646
|
/**
|
|
47652
47647
|
* `val-invite-member-modal` — invitación de miembros a una organización.
|
|
@@ -47677,7 +47672,7 @@ class InviteMemberModalComponent {
|
|
|
47677
47672
|
/** Ids de miembros ya existentes (excluidos de los resultados). */
|
|
47678
47673
|
this.existingMemberIds = [];
|
|
47679
47674
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
47680
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
47675
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$6;
|
|
47681
47676
|
this.query = signal('');
|
|
47682
47677
|
this.searching = signal(false);
|
|
47683
47678
|
this.searchResults = signal([]);
|
|
@@ -47724,8 +47719,8 @@ class InviteMemberModalComponent {
|
|
|
47724
47719
|
state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
47725
47720
|
}));
|
|
47726
47721
|
this.searchSubject = new Subject();
|
|
47727
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
47728
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
47722
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
|
|
47723
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$6, INVITE_MEMBER_MODAL_I18N);
|
|
47729
47724
|
}
|
|
47730
47725
|
this.loadRoles();
|
|
47731
47726
|
this.roleSub = this.roleControl.valueChanges.subscribe(v => {
|
|
@@ -48214,7 +48209,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
|
|
|
48214
48209
|
},
|
|
48215
48210
|
};
|
|
48216
48211
|
|
|
48217
|
-
const DEFAULT_NAMESPACE$
|
|
48212
|
+
const DEFAULT_NAMESPACE$5 = 'Settings.MemberDetail';
|
|
48218
48213
|
/**
|
|
48219
48214
|
* `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
|
|
48220
48215
|
* permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
|
|
@@ -48251,7 +48246,7 @@ class MemberDetailModalComponent {
|
|
|
48251
48246
|
{ id: 'admin', name: 'admin' },
|
|
48252
48247
|
];
|
|
48253
48248
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
48254
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
48249
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$5;
|
|
48255
48250
|
this.working = signal(false);
|
|
48256
48251
|
this.currentRole = signal('');
|
|
48257
48252
|
this.roleControl = new FormControl('');
|
|
@@ -48291,8 +48286,8 @@ class MemberDetailModalComponent {
|
|
|
48291
48286
|
type: 'button',
|
|
48292
48287
|
state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
48293
48288
|
}));
|
|
48294
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
48295
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
48289
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
|
|
48290
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_DETAIL_MODAL_I18N);
|
|
48296
48291
|
}
|
|
48297
48292
|
}
|
|
48298
48293
|
ngOnInit() {
|
|
@@ -48629,7 +48624,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
|
|
|
48629
48624
|
},
|
|
48630
48625
|
};
|
|
48631
48626
|
|
|
48632
|
-
const DEFAULT_NAMESPACE$
|
|
48627
|
+
const DEFAULT_NAMESPACE$4 = 'Settings.TransferModal';
|
|
48633
48628
|
/**
|
|
48634
48629
|
* `val-transfer-ownership-modal` — selector de miembro para transferir la
|
|
48635
48630
|
* propiedad de la organización. Promovido desde `showcase` bajo el proceso de
|
|
@@ -48653,7 +48648,7 @@ class TransferOwnershipModalComponent {
|
|
|
48653
48648
|
/** Lista de miembros transferibles (todos excepto el owner actual). */
|
|
48654
48649
|
this.members = [];
|
|
48655
48650
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
48656
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
48651
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$4;
|
|
48657
48652
|
this.query = signal('');
|
|
48658
48653
|
this.selectedId = signal('');
|
|
48659
48654
|
this.filtered = computed(() => {
|
|
@@ -48665,8 +48660,8 @@ class TransferOwnershipModalComponent {
|
|
|
48665
48660
|
m.userId.toLowerCase().includes(q));
|
|
48666
48661
|
});
|
|
48667
48662
|
this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
|
|
48668
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
48669
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
48663
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
|
|
48664
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$4, TRANSFER_OWNERSHIP_MODAL_I18N);
|
|
48670
48665
|
}
|
|
48671
48666
|
}
|
|
48672
48667
|
ngOnInit() {
|
|
@@ -48851,849 +48846,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
48851
48846
|
type: Input
|
|
48852
48847
|
}] } });
|
|
48853
48848
|
|
|
48854
|
-
/**
|
|
48855
|
-
* Defaults i18n (es/en) embebidos en `val-member-import-modal` (ADR-023).
|
|
48856
|
-
* Auto-registrados si el consumer no proveyó el namespace `Settings.ImportModal`.
|
|
48857
|
-
*/
|
|
48858
|
-
const MEMBER_IMPORT_MODAL_I18N = {
|
|
48859
|
-
es: {
|
|
48860
|
-
title: 'Importar miembros',
|
|
48861
|
-
hint: 'Pega un CSV con columnas: email, nombre, rol (una fila por persona).',
|
|
48862
|
-
csvPlaceholder: 'email,nombre,rol\nana@x.com,Ana,editor\nbeto@x.com,Beto,viewer',
|
|
48863
|
-
rolesAvailable: 'Roles disponibles:',
|
|
48864
|
-
onConflictLabel: 'Si el email ya existe',
|
|
48865
|
-
conflictAssign: 'Asignar el rol',
|
|
48866
|
-
conflictSkip: 'Omitir',
|
|
48867
|
-
conflictError: 'Marcar error',
|
|
48868
|
-
sendActivation: 'Enviar email de activación a las cuentas nuevas',
|
|
48869
|
-
submit: 'Importar',
|
|
48870
|
-
submitting: 'Importando...',
|
|
48871
|
-
parseError: 'No se pudo interpretar el CSV. Revisa el formato.',
|
|
48872
|
-
noRows: 'Pega al menos una fila válida (email y rol).',
|
|
48873
|
-
summary: 'Resumen',
|
|
48874
|
-
created: 'creados',
|
|
48875
|
-
assigned: 'asignados',
|
|
48876
|
-
skipped: 'omitidos',
|
|
48877
|
-
failed: 'con error',
|
|
48878
|
-
colEmail: 'Email',
|
|
48879
|
-
colStatus: 'Estado',
|
|
48880
|
-
colDetail: 'Detalle',
|
|
48881
|
-
activationSent: 'activación enviada',
|
|
48882
|
-
activationPending: 'activación pendiente',
|
|
48883
|
-
again: 'Importar otro lote',
|
|
48884
|
-
close: 'Cerrar',
|
|
48885
|
-
genericError: 'No se pudo completar la importación. Intenta de nuevo.',
|
|
48886
|
-
},
|
|
48887
|
-
en: {
|
|
48888
|
-
title: 'Import members',
|
|
48889
|
-
hint: 'Paste a CSV with columns: email, name, role (one row per person).',
|
|
48890
|
-
csvPlaceholder: 'email,name,role\nana@x.com,Ana,editor\nbeto@x.com,Beto,viewer',
|
|
48891
|
-
rolesAvailable: 'Available roles:',
|
|
48892
|
-
onConflictLabel: 'If the email already exists',
|
|
48893
|
-
conflictAssign: 'Assign the role',
|
|
48894
|
-
conflictSkip: 'Skip',
|
|
48895
|
-
conflictError: 'Mark as error',
|
|
48896
|
-
sendActivation: 'Send activation email to new accounts',
|
|
48897
|
-
submit: 'Import',
|
|
48898
|
-
submitting: 'Importing...',
|
|
48899
|
-
parseError: "Couldn't parse the CSV. Check the format.",
|
|
48900
|
-
noRows: 'Paste at least one valid row (email and role).',
|
|
48901
|
-
summary: 'Summary',
|
|
48902
|
-
created: 'created',
|
|
48903
|
-
assigned: 'assigned',
|
|
48904
|
-
skipped: 'skipped',
|
|
48905
|
-
failed: 'failed',
|
|
48906
|
-
colEmail: 'Email',
|
|
48907
|
-
colStatus: 'Status',
|
|
48908
|
-
colDetail: 'Detail',
|
|
48909
|
-
activationSent: 'activation sent',
|
|
48910
|
-
activationPending: 'activation pending',
|
|
48911
|
-
again: 'Import another batch',
|
|
48912
|
-
close: 'Close',
|
|
48913
|
-
genericError: "Couldn't complete the import. Try again.",
|
|
48914
|
-
},
|
|
48915
|
-
};
|
|
48916
|
-
|
|
48917
|
-
const DEFAULT_NAMESPACE$6 = 'Settings.ImportModal';
|
|
48918
|
-
/**
|
|
48919
|
-
* `val-member-import-modal` — panel de carga masiva de miembros (ADR-023 fase 6).
|
|
48920
|
-
*
|
|
48921
|
-
* El admin pega un CSV (`email,nombre,rol`); el modal lo parsea a filas y llama
|
|
48922
|
-
* `OrgService.importMembers`. Muestra el resultado por-fila (partial success):
|
|
48923
|
-
* `created` / `assigned` / `skipped` / `error`. Header canónico (Regla #5).
|
|
48924
|
-
*
|
|
48925
|
-
* Auto-registra defaults i18n (es/en) en `Settings.ImportModal`.
|
|
48926
|
-
*/
|
|
48927
|
-
class MemberImportModalComponent {
|
|
48928
|
-
constructor() {
|
|
48929
|
-
this.i18n = inject(I18nService);
|
|
48930
|
-
this.orgService = inject(OrgService);
|
|
48931
|
-
/** Org destino del import. */
|
|
48932
|
-
this.orgId = '';
|
|
48933
|
-
/** Nombres de roles disponibles (para el hint). Opcional. */
|
|
48934
|
-
this.availableRoles = [];
|
|
48935
|
-
/** Namespace i18n. */
|
|
48936
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$6;
|
|
48937
|
-
this.csv = '';
|
|
48938
|
-
this.onConflict = 'assignRole';
|
|
48939
|
-
this.sendActivation = true;
|
|
48940
|
-
this.submitting = signal(false);
|
|
48941
|
-
this.errorMsg = signal('');
|
|
48942
|
-
this.results = signal(null);
|
|
48943
|
-
this.rolesHint = computed(() => this.availableRoles.length ? `${this.t('rolesAvailable')} ${this.availableRoles.join(', ')}` : '');
|
|
48944
|
-
this.summaryLabel = computed(() => {
|
|
48945
|
-
const s = this.results()?.summary;
|
|
48946
|
-
if (!s)
|
|
48947
|
-
return this.t('summary');
|
|
48948
|
-
return `${this.t('summary')}: ${s.created} ${this.t('created')} · ${s.assigned} ${this.t('assigned')} · ${s.skipped} ${this.t('skipped')} · ${s.failed} ${this.t('failed')}`;
|
|
48949
|
-
});
|
|
48950
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
|
|
48951
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$6, MEMBER_IMPORT_MODAL_I18N);
|
|
48952
|
-
}
|
|
48953
|
-
}
|
|
48954
|
-
ngOnInit() {
|
|
48955
|
-
if (!this.i18n.hasNamespace(this.i18nNamespace)) {
|
|
48956
|
-
this.i18n.registerContent(this.i18nNamespace, MEMBER_IMPORT_MODAL_I18N);
|
|
48957
|
-
}
|
|
48958
|
-
}
|
|
48959
|
-
/** Parsea el CSV pegado a filas {email,name,roleName}. Tolera header. */
|
|
48960
|
-
parseCsv() {
|
|
48961
|
-
const rows = [];
|
|
48962
|
-
for (const raw of this.csv.split(/\r?\n/)) {
|
|
48963
|
-
const line = raw.trim();
|
|
48964
|
-
if (!line)
|
|
48965
|
-
continue;
|
|
48966
|
-
const cols = line.split(',').map(c => c.trim());
|
|
48967
|
-
const email = (cols[0] ?? '').toLowerCase();
|
|
48968
|
-
// Saltar header (primera col "email") o líneas sin @.
|
|
48969
|
-
if (!email || !email.includes('@'))
|
|
48970
|
-
continue;
|
|
48971
|
-
rows.push({ email, name: cols[1] ?? '', roleName: cols[2] ?? '' });
|
|
48972
|
-
}
|
|
48973
|
-
return rows;
|
|
48974
|
-
}
|
|
48975
|
-
submit() {
|
|
48976
|
-
this.errorMsg.set('');
|
|
48977
|
-
const users = this.parseCsv();
|
|
48978
|
-
if (users.length === 0) {
|
|
48979
|
-
this.errorMsg.set(this.t('noRows'));
|
|
48980
|
-
return;
|
|
48981
|
-
}
|
|
48982
|
-
if (users.some(u => !u.roleName)) {
|
|
48983
|
-
this.errorMsg.set(this.t('parseError'));
|
|
48984
|
-
return;
|
|
48985
|
-
}
|
|
48986
|
-
this.submitting.set(true);
|
|
48987
|
-
this.orgService
|
|
48988
|
-
.importMembers(this.orgId, {
|
|
48989
|
-
users,
|
|
48990
|
-
onConflict: this.onConflict,
|
|
48991
|
-
sendActivation: this.sendActivation,
|
|
48992
|
-
})
|
|
48993
|
-
.subscribe({
|
|
48994
|
-
next: res => {
|
|
48995
|
-
this.submitting.set(false);
|
|
48996
|
-
this.results.set(res);
|
|
48997
|
-
// Refrescar lista del consumer si hubo altas/asignaciones efectivas.
|
|
48998
|
-
if (res.summary.created + res.summary.assigned > 0) {
|
|
48999
|
-
this.onSuccess?.();
|
|
49000
|
-
}
|
|
49001
|
-
},
|
|
49002
|
-
error: err => {
|
|
49003
|
-
this.submitting.set(false);
|
|
49004
|
-
this.errorMsg.set(err?.error?.message ?? this.t('genericError'));
|
|
49005
|
-
},
|
|
49006
|
-
});
|
|
49007
|
-
}
|
|
49008
|
-
statusLabel(r) {
|
|
49009
|
-
const base = this.t(r.status === 'error' ? 'failed' : r.status);
|
|
49010
|
-
if (r.status === 'created' && r.activation) {
|
|
49011
|
-
return `${base} · ${this.t(r.activation === 'sent' ? 'activationSent' : 'activationPending')}`;
|
|
49012
|
-
}
|
|
49013
|
-
return base;
|
|
49014
|
-
}
|
|
49015
|
-
reset() {
|
|
49016
|
-
this.csv = '';
|
|
49017
|
-
this.errorMsg.set('');
|
|
49018
|
-
this.results.set(null);
|
|
49019
|
-
}
|
|
49020
|
-
dismiss() {
|
|
49021
|
-
this._modalRef?.dismiss(null, 'cancel');
|
|
49022
|
-
}
|
|
49023
|
-
t(key) {
|
|
49024
|
-
return this.i18n.t(key, this.i18nNamespace);
|
|
49025
|
-
}
|
|
49026
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MemberImportModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
49027
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: MemberImportModalComponent, isStandalone: true, selector: "val-member-import-modal", inputs: { orgId: "orgId", availableRoles: "availableRoles", onSuccess: "onSuccess", _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
|
|
49028
|
-
<ion-header>
|
|
49029
|
-
<ion-toolbar>
|
|
49030
|
-
<ion-buttons slot="end">
|
|
49031
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49032
|
-
<strong>{{ t('close') }}</strong>
|
|
49033
|
-
</ion-button>
|
|
49034
|
-
</ion-buttons>
|
|
49035
|
-
</ion-toolbar>
|
|
49036
|
-
</ion-header>
|
|
49037
|
-
|
|
49038
|
-
<ion-content class="ion-padding">
|
|
49039
|
-
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
49040
|
-
|
|
49041
|
-
@if (!results()) {
|
|
49042
|
-
<val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
|
|
49043
|
-
|
|
49044
|
-
@if (rolesHint()) {
|
|
49045
|
-
<val-text [props]="{ content: rolesHint(), size: 'small', color: 'medium', bold: false }" />
|
|
49046
|
-
}
|
|
49047
|
-
|
|
49048
|
-
<ion-textarea
|
|
49049
|
-
class="import-csv"
|
|
49050
|
-
[autoGrow]="true"
|
|
49051
|
-
[rows]="6"
|
|
49052
|
-
[placeholder]="t('csvPlaceholder')"
|
|
49053
|
-
[(ngModel)]="csv"
|
|
49054
|
-
/>
|
|
49055
|
-
|
|
49056
|
-
<ion-select
|
|
49057
|
-
class="import-conflict"
|
|
49058
|
-
label="{{ t('onConflictLabel') }}"
|
|
49059
|
-
labelPlacement="stacked"
|
|
49060
|
-
interface="popover"
|
|
49061
|
-
[(ngModel)]="onConflict"
|
|
49062
|
-
>
|
|
49063
|
-
<ion-select-option value="assignRole">{{ t('conflictAssign') }}</ion-select-option>
|
|
49064
|
-
<ion-select-option value="skip">{{ t('conflictSkip') }}</ion-select-option>
|
|
49065
|
-
<ion-select-option value="error">{{ t('conflictError') }}</ion-select-option>
|
|
49066
|
-
</ion-select>
|
|
49067
|
-
|
|
49068
|
-
<ion-checkbox class="import-activation" labelPlacement="end" [(ngModel)]="sendActivation">
|
|
49069
|
-
{{ t('sendActivation') }}
|
|
49070
|
-
</ion-checkbox>
|
|
49071
|
-
|
|
49072
|
-
@if (errorMsg()) {
|
|
49073
|
-
<p class="import-error">{{ errorMsg() }}</p>
|
|
49074
|
-
}
|
|
49075
|
-
|
|
49076
|
-
<div class="import-actions">
|
|
49077
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
49078
|
-
@if (submitting()) {
|
|
49079
|
-
<ion-spinner name="dots" />
|
|
49080
|
-
{{ t('submitting') }}
|
|
49081
|
-
} @else {
|
|
49082
|
-
{{ t('submit') }}
|
|
49083
|
-
}
|
|
49084
|
-
</ion-button>
|
|
49085
|
-
</div>
|
|
49086
|
-
} @else {
|
|
49087
|
-
<!-- Resultados -->
|
|
49088
|
-
<val-title [props]="{ content: summaryLabel(), size: 'large', color: '', bold: true }" />
|
|
49089
|
-
<div class="import-results">
|
|
49090
|
-
@for (r of results()!.results; track r.email) {
|
|
49091
|
-
<div class="import-row import-row--{{ r.status }}">
|
|
49092
|
-
<span class="import-row__email">{{ r.email }}</span>
|
|
49093
|
-
<span class="import-row__status">{{ statusLabel(r) }}</span>
|
|
49094
|
-
@if (r.reason) {
|
|
49095
|
-
<span class="import-row__reason">{{ r.reason }}</span>
|
|
49096
|
-
}
|
|
49097
|
-
</div>
|
|
49098
|
-
}
|
|
49099
|
-
</div>
|
|
49100
|
-
<div class="import-actions">
|
|
49101
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="reset()">
|
|
49102
|
-
{{ t('again') }}
|
|
49103
|
-
</ion-button>
|
|
49104
|
-
</div>
|
|
49105
|
-
}
|
|
49106
|
-
</ion-content>
|
|
49107
|
-
`, isInline: true, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.import-csv{--background: var(--ion-color-light);border-radius:10px;margin-bottom:16px;font-family:monospace;font-size:13px}.import-conflict{margin-bottom:12px}.import-activation{display:block;margin-bottom:16px;font-size:14px}.import-error{margin:0 0 12px;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.import-actions{margin-top:8px}.import-results{display:flex;flex-direction:column;gap:6px;margin:12px 0}.import-row{display:grid;grid-template-columns:1fr auto;grid-row-gap:2px;align-items:center;padding:10px 12px;border-radius:8px;border-left:3px solid var(--ion-color-medium);background:var(--ion-color-light);font-size:14px}.import-row--created{border-left-color:var(--ion-color-success, #2dd36f)}.import-row--assigned{border-left-color:var(--ion-color-primary, #7026df)}.import-row--skipped{border-left-color:var(--ion-color-medium, #92949c)}.import-row--error{border-left-color:var(--ion-color-danger, #c0392b)}.import-row__email{font-weight:600;color:var(--ion-color-dark)}.import-row__status{text-align:right;color:var(--ion-color-medium-shade, #6b6e76)}.import-row__reason{grid-column:1 / -1;font-size:12px;color:var(--ion-color-danger, #c0392b)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { 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"] }] }); }
|
|
49108
|
-
}
|
|
49109
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MemberImportModalComponent, decorators: [{
|
|
49110
|
-
type: Component,
|
|
49111
|
-
args: [{ selector: 'val-member-import-modal', standalone: true, imports: [
|
|
49112
|
-
FormsModule,
|
|
49113
|
-
IonHeader,
|
|
49114
|
-
IonToolbar,
|
|
49115
|
-
IonButtons,
|
|
49116
|
-
IonButton,
|
|
49117
|
-
IonContent,
|
|
49118
|
-
IonTextarea,
|
|
49119
|
-
IonSelect,
|
|
49120
|
-
IonSelectOption,
|
|
49121
|
-
IonCheckbox,
|
|
49122
|
-
IonSpinner,
|
|
49123
|
-
DisplayComponent,
|
|
49124
|
-
TextComponent,
|
|
49125
|
-
TitleComponent,
|
|
49126
|
-
], template: `
|
|
49127
|
-
<ion-header>
|
|
49128
|
-
<ion-toolbar>
|
|
49129
|
-
<ion-buttons slot="end">
|
|
49130
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49131
|
-
<strong>{{ t('close') }}</strong>
|
|
49132
|
-
</ion-button>
|
|
49133
|
-
</ion-buttons>
|
|
49134
|
-
</ion-toolbar>
|
|
49135
|
-
</ion-header>
|
|
49136
|
-
|
|
49137
|
-
<ion-content class="ion-padding">
|
|
49138
|
-
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
49139
|
-
|
|
49140
|
-
@if (!results()) {
|
|
49141
|
-
<val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
|
|
49142
|
-
|
|
49143
|
-
@if (rolesHint()) {
|
|
49144
|
-
<val-text [props]="{ content: rolesHint(), size: 'small', color: 'medium', bold: false }" />
|
|
49145
|
-
}
|
|
49146
|
-
|
|
49147
|
-
<ion-textarea
|
|
49148
|
-
class="import-csv"
|
|
49149
|
-
[autoGrow]="true"
|
|
49150
|
-
[rows]="6"
|
|
49151
|
-
[placeholder]="t('csvPlaceholder')"
|
|
49152
|
-
[(ngModel)]="csv"
|
|
49153
|
-
/>
|
|
49154
|
-
|
|
49155
|
-
<ion-select
|
|
49156
|
-
class="import-conflict"
|
|
49157
|
-
label="{{ t('onConflictLabel') }}"
|
|
49158
|
-
labelPlacement="stacked"
|
|
49159
|
-
interface="popover"
|
|
49160
|
-
[(ngModel)]="onConflict"
|
|
49161
|
-
>
|
|
49162
|
-
<ion-select-option value="assignRole">{{ t('conflictAssign') }}</ion-select-option>
|
|
49163
|
-
<ion-select-option value="skip">{{ t('conflictSkip') }}</ion-select-option>
|
|
49164
|
-
<ion-select-option value="error">{{ t('conflictError') }}</ion-select-option>
|
|
49165
|
-
</ion-select>
|
|
49166
|
-
|
|
49167
|
-
<ion-checkbox class="import-activation" labelPlacement="end" [(ngModel)]="sendActivation">
|
|
49168
|
-
{{ t('sendActivation') }}
|
|
49169
|
-
</ion-checkbox>
|
|
49170
|
-
|
|
49171
|
-
@if (errorMsg()) {
|
|
49172
|
-
<p class="import-error">{{ errorMsg() }}</p>
|
|
49173
|
-
}
|
|
49174
|
-
|
|
49175
|
-
<div class="import-actions">
|
|
49176
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
49177
|
-
@if (submitting()) {
|
|
49178
|
-
<ion-spinner name="dots" />
|
|
49179
|
-
{{ t('submitting') }}
|
|
49180
|
-
} @else {
|
|
49181
|
-
{{ t('submit') }}
|
|
49182
|
-
}
|
|
49183
|
-
</ion-button>
|
|
49184
|
-
</div>
|
|
49185
|
-
} @else {
|
|
49186
|
-
<!-- Resultados -->
|
|
49187
|
-
<val-title [props]="{ content: summaryLabel(), size: 'large', color: '', bold: true }" />
|
|
49188
|
-
<div class="import-results">
|
|
49189
|
-
@for (r of results()!.results; track r.email) {
|
|
49190
|
-
<div class="import-row import-row--{{ r.status }}">
|
|
49191
|
-
<span class="import-row__email">{{ r.email }}</span>
|
|
49192
|
-
<span class="import-row__status">{{ statusLabel(r) }}</span>
|
|
49193
|
-
@if (r.reason) {
|
|
49194
|
-
<span class="import-row__reason">{{ r.reason }}</span>
|
|
49195
|
-
}
|
|
49196
|
-
</div>
|
|
49197
|
-
}
|
|
49198
|
-
</div>
|
|
49199
|
-
<div class="import-actions">
|
|
49200
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="reset()">
|
|
49201
|
-
{{ t('again') }}
|
|
49202
|
-
</ion-button>
|
|
49203
|
-
</div>
|
|
49204
|
-
}
|
|
49205
|
-
</ion-content>
|
|
49206
|
-
`, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.import-csv{--background: var(--ion-color-light);border-radius:10px;margin-bottom:16px;font-family:monospace;font-size:13px}.import-conflict{margin-bottom:12px}.import-activation{display:block;margin-bottom:16px;font-size:14px}.import-error{margin:0 0 12px;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.import-actions{margin-top:8px}.import-results{display:flex;flex-direction:column;gap:6px;margin:12px 0}.import-row{display:grid;grid-template-columns:1fr auto;grid-row-gap:2px;align-items:center;padding:10px 12px;border-radius:8px;border-left:3px solid var(--ion-color-medium);background:var(--ion-color-light);font-size:14px}.import-row--created{border-left-color:var(--ion-color-success, #2dd36f)}.import-row--assigned{border-left-color:var(--ion-color-primary, #7026df)}.import-row--skipped{border-left-color:var(--ion-color-medium, #92949c)}.import-row--error{border-left-color:var(--ion-color-danger, #c0392b)}.import-row__email{font-weight:600;color:var(--ion-color-dark)}.import-row__status{text-align:right;color:var(--ion-color-medium-shade, #6b6e76)}.import-row__reason{grid-column:1 / -1;font-size:12px;color:var(--ion-color-danger, #c0392b)}\n"] }]
|
|
49207
|
-
}], ctorParameters: () => [], propDecorators: { orgId: [{
|
|
49208
|
-
type: Input
|
|
49209
|
-
}], availableRoles: [{
|
|
49210
|
-
type: Input
|
|
49211
|
-
}], onSuccess: [{
|
|
49212
|
-
type: Input
|
|
49213
|
-
}], _modalRef: [{
|
|
49214
|
-
type: Input
|
|
49215
|
-
}], i18nNamespace: [{
|
|
49216
|
-
type: Input
|
|
49217
|
-
}] } });
|
|
49218
|
-
|
|
49219
|
-
/**
|
|
49220
|
-
* Cliente HTTP de Client API Keys org-scopeadas (ADR-023). La org activa se
|
|
49221
|
-
* resuelve en backend desde el JWT, así que el path no la incluye.
|
|
49222
|
-
*/
|
|
49223
|
-
class ApiKeyService {
|
|
49224
|
-
constructor(config, http) {
|
|
49225
|
-
this.config = config;
|
|
49226
|
-
this.http = http;
|
|
49227
|
-
}
|
|
49228
|
-
get baseUrl() {
|
|
49229
|
-
return `${this.config.apiUrl}/org/api-keys`;
|
|
49230
|
-
}
|
|
49231
|
-
/** Lista las API keys de la org activa. */
|
|
49232
|
-
list() {
|
|
49233
|
-
return this.http.get(`${this.baseUrl}/`).pipe(map$1(r => r.keys ?? []));
|
|
49234
|
-
}
|
|
49235
|
-
/** Crea una API key. La respuesta incluye el secreto (se muestra UNA vez). */
|
|
49236
|
-
create(req) {
|
|
49237
|
-
return this.http.post(`${this.baseUrl}/`, req);
|
|
49238
|
-
}
|
|
49239
|
-
/** Revoca (elimina) una API key por id. */
|
|
49240
|
-
revoke(keyId) {
|
|
49241
|
-
return this.http.delete(`${this.baseUrl}/${keyId}`);
|
|
49242
|
-
}
|
|
49243
|
-
/** Catálogo de permisos asignables a una key. */
|
|
49244
|
-
availablePermissions() {
|
|
49245
|
-
return this.http
|
|
49246
|
-
.get(`${this.baseUrl}/permissions`)
|
|
49247
|
-
.pipe(map$1(r => r.permissions ?? []));
|
|
49248
|
-
}
|
|
49249
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
49250
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, providedIn: 'root' }); }
|
|
49251
|
-
}
|
|
49252
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, decorators: [{
|
|
49253
|
-
type: Injectable,
|
|
49254
|
-
args: [{ providedIn: 'root' }]
|
|
49255
|
-
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
49256
|
-
type: Inject,
|
|
49257
|
-
args: [VALTECH_AUTH_CONFIG]
|
|
49258
|
-
}] }, { type: i1$3.HttpClient }] });
|
|
49259
|
-
|
|
49260
|
-
/**
|
|
49261
|
-
* Defaults i18n (es/en) embebidos en `val-api-keys-modal` (ADR-023).
|
|
49262
|
-
* Auto-registrados si el consumer no proveyó el namespace `Settings.ApiKeysModal`.
|
|
49263
|
-
*/
|
|
49264
|
-
const API_KEYS_MODAL_I18N = {
|
|
49265
|
-
es: {
|
|
49266
|
-
title: 'Client API Keys',
|
|
49267
|
-
hint: 'Llaves M2M org-scopeadas para integraciones (p. ej. importar miembros).',
|
|
49268
|
-
empty: 'Aún no hay API keys en esta organización.',
|
|
49269
|
-
newKey: 'Nueva API key',
|
|
49270
|
-
nameLabel: 'Nombre',
|
|
49271
|
-
namePlaceholder: 'Ej: Integración RRHH',
|
|
49272
|
-
permissionsLabel: 'Permisos',
|
|
49273
|
-
expiresLabel: 'Expira en (días, opcional)',
|
|
49274
|
-
create: 'Crear key',
|
|
49275
|
-
creating: 'Creando...',
|
|
49276
|
-
cancel: 'Cancelar',
|
|
49277
|
-
revoke: 'Revocar',
|
|
49278
|
-
revokeConfirm: '¿Revocar esta API key? Las integraciones que la usen dejarán de funcionar.',
|
|
49279
|
-
secretTitle: 'Copia tu API key ahora',
|
|
49280
|
-
secretWarning: 'Este secreto se muestra una sola vez. Guárdalo en un lugar seguro.',
|
|
49281
|
-
copy: 'Copiar',
|
|
49282
|
-
copied: 'Copiada',
|
|
49283
|
-
done: 'Listo',
|
|
49284
|
-
statusActive: 'activa',
|
|
49285
|
-
lastUsed: 'Último uso',
|
|
49286
|
-
never: 'nunca',
|
|
49287
|
-
permsCount: 'permisos',
|
|
49288
|
-
noName: 'El nombre es obligatorio.',
|
|
49289
|
-
noPerms: 'Selecciona al menos un permiso.',
|
|
49290
|
-
close: 'Cerrar',
|
|
49291
|
-
genericError: 'No se pudo completar la operación. Intenta de nuevo.',
|
|
49292
|
-
},
|
|
49293
|
-
en: {
|
|
49294
|
-
title: 'Client API Keys',
|
|
49295
|
-
hint: 'Org-scoped M2M keys for integrations (e.g. member import).',
|
|
49296
|
-
empty: 'No API keys in this organization yet.',
|
|
49297
|
-
newKey: 'New API key',
|
|
49298
|
-
nameLabel: 'Name',
|
|
49299
|
-
namePlaceholder: 'E.g: HR integration',
|
|
49300
|
-
permissionsLabel: 'Permissions',
|
|
49301
|
-
expiresLabel: 'Expires in (days, optional)',
|
|
49302
|
-
create: 'Create key',
|
|
49303
|
-
creating: 'Creating...',
|
|
49304
|
-
cancel: 'Cancel',
|
|
49305
|
-
revoke: 'Revoke',
|
|
49306
|
-
revokeConfirm: 'Revoke this API key? Integrations using it will stop working.',
|
|
49307
|
-
secretTitle: 'Copy your API key now',
|
|
49308
|
-
secretWarning: 'This secret is shown only once. Store it somewhere safe.',
|
|
49309
|
-
copy: 'Copy',
|
|
49310
|
-
copied: 'Copied',
|
|
49311
|
-
done: 'Done',
|
|
49312
|
-
statusActive: 'active',
|
|
49313
|
-
lastUsed: 'Last used',
|
|
49314
|
-
never: 'never',
|
|
49315
|
-
permsCount: 'permissions',
|
|
49316
|
-
noName: 'Name is required.',
|
|
49317
|
-
noPerms: 'Select at least one permission.',
|
|
49318
|
-
close: 'Close',
|
|
49319
|
-
genericError: "Couldn't complete the operation. Try again.",
|
|
49320
|
-
},
|
|
49321
|
-
};
|
|
49322
|
-
|
|
49323
|
-
const DEFAULT_NAMESPACE$5 = 'Settings.ApiKeysModal';
|
|
49324
|
-
/**
|
|
49325
|
-
* `val-api-keys-modal` — gestión de Client API Keys org-scopeadas (ADR-023 fase 6).
|
|
49326
|
-
*
|
|
49327
|
-
* Lista las keys de la org, permite crear (mostrando el secreto UNA vez con copia)
|
|
49328
|
-
* y revocar. Pensado para habilitar integraciones M2M del import de miembros, pero
|
|
49329
|
-
* sirve a cualquier permiso del catálogo. Header canónico (Regla #5).
|
|
49330
|
-
*/
|
|
49331
|
-
class ApiKeysModalComponent {
|
|
49332
|
-
constructor() {
|
|
49333
|
-
this.i18n = inject(I18nService);
|
|
49334
|
-
this.apiKeys = inject(ApiKeyService);
|
|
49335
|
-
/** Namespace i18n. */
|
|
49336
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$5;
|
|
49337
|
-
this.keys = signal([]);
|
|
49338
|
-
this.availablePermissions = signal([]);
|
|
49339
|
-
this.loading = signal(true);
|
|
49340
|
-
this.showCreate = signal(false);
|
|
49341
|
-
this.submitting = signal(false);
|
|
49342
|
-
this.errorMsg = signal('');
|
|
49343
|
-
this.secret = signal(null);
|
|
49344
|
-
this.copied = signal(false);
|
|
49345
|
-
this.name = '';
|
|
49346
|
-
this.expiresInDays = null;
|
|
49347
|
-
this.selectedPerms = signal(new Set());
|
|
49348
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
|
|
49349
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$5, API_KEYS_MODAL_I18N);
|
|
49350
|
-
}
|
|
49351
|
-
}
|
|
49352
|
-
ngOnInit() {
|
|
49353
|
-
if (!this.i18n.hasNamespace(this.i18nNamespace)) {
|
|
49354
|
-
this.i18n.registerContent(this.i18nNamespace, API_KEYS_MODAL_I18N);
|
|
49355
|
-
}
|
|
49356
|
-
this.refresh();
|
|
49357
|
-
this.apiKeys.availablePermissions().subscribe({
|
|
49358
|
-
next: perms => this.availablePermissions.set(perms),
|
|
49359
|
-
error: () => { },
|
|
49360
|
-
});
|
|
49361
|
-
}
|
|
49362
|
-
refresh() {
|
|
49363
|
-
this.loading.set(true);
|
|
49364
|
-
this.apiKeys.list().subscribe({
|
|
49365
|
-
next: keys => {
|
|
49366
|
-
this.keys.set(keys);
|
|
49367
|
-
this.loading.set(false);
|
|
49368
|
-
},
|
|
49369
|
-
error: () => this.loading.set(false),
|
|
49370
|
-
});
|
|
49371
|
-
}
|
|
49372
|
-
openCreate() {
|
|
49373
|
-
this.errorMsg.set('');
|
|
49374
|
-
this.name = '';
|
|
49375
|
-
this.expiresInDays = null;
|
|
49376
|
-
this.selectedPerms.set(new Set());
|
|
49377
|
-
this.showCreate.set(true);
|
|
49378
|
-
}
|
|
49379
|
-
togglePerm(perm, ev) {
|
|
49380
|
-
const checked = ev.detail?.checked ?? false;
|
|
49381
|
-
const next = new Set(this.selectedPerms());
|
|
49382
|
-
if (checked) {
|
|
49383
|
-
next.add(perm);
|
|
49384
|
-
}
|
|
49385
|
-
else {
|
|
49386
|
-
next.delete(perm);
|
|
49387
|
-
}
|
|
49388
|
-
this.selectedPerms.set(next);
|
|
49389
|
-
}
|
|
49390
|
-
submit() {
|
|
49391
|
-
this.errorMsg.set('');
|
|
49392
|
-
if (!this.name.trim()) {
|
|
49393
|
-
this.errorMsg.set(this.t('noName'));
|
|
49394
|
-
return;
|
|
49395
|
-
}
|
|
49396
|
-
const permissions = Array.from(this.selectedPerms());
|
|
49397
|
-
if (permissions.length === 0) {
|
|
49398
|
-
this.errorMsg.set(this.t('noPerms'));
|
|
49399
|
-
return;
|
|
49400
|
-
}
|
|
49401
|
-
this.submitting.set(true);
|
|
49402
|
-
this.apiKeys
|
|
49403
|
-
.create({
|
|
49404
|
-
name: this.name.trim(),
|
|
49405
|
-
permissions,
|
|
49406
|
-
expiresInDays: this.expiresInDays && this.expiresInDays > 0 ? this.expiresInDays : undefined,
|
|
49407
|
-
})
|
|
49408
|
-
.subscribe({
|
|
49409
|
-
next: res => {
|
|
49410
|
-
this.submitting.set(false);
|
|
49411
|
-
this.showCreate.set(false);
|
|
49412
|
-
this.secret.set(res.key);
|
|
49413
|
-
},
|
|
49414
|
-
error: err => {
|
|
49415
|
-
this.submitting.set(false);
|
|
49416
|
-
this.errorMsg.set(err?.error?.message ?? this.t('genericError'));
|
|
49417
|
-
},
|
|
49418
|
-
});
|
|
49419
|
-
}
|
|
49420
|
-
revoke(key) {
|
|
49421
|
-
if (!confirm(this.t('revokeConfirm')))
|
|
49422
|
-
return;
|
|
49423
|
-
this.apiKeys.revoke(key.keyId).subscribe({
|
|
49424
|
-
next: () => this.refresh(),
|
|
49425
|
-
error: err => this.errorMsg.set(err?.error?.message ?? this.t('genericError')),
|
|
49426
|
-
});
|
|
49427
|
-
}
|
|
49428
|
-
async copy(secret) {
|
|
49429
|
-
try {
|
|
49430
|
-
await navigator.clipboard.writeText(secret);
|
|
49431
|
-
this.copied.set(true);
|
|
49432
|
-
}
|
|
49433
|
-
catch {
|
|
49434
|
-
// clipboard no disponible — el user puede seleccionar el texto manualmente
|
|
49435
|
-
}
|
|
49436
|
-
}
|
|
49437
|
-
finishSecret() {
|
|
49438
|
-
this.secret.set(null);
|
|
49439
|
-
this.copied.set(false);
|
|
49440
|
-
this.refresh();
|
|
49441
|
-
}
|
|
49442
|
-
dismiss() {
|
|
49443
|
-
this._modalRef?.dismiss(null, 'cancel');
|
|
49444
|
-
}
|
|
49445
|
-
t(key) {
|
|
49446
|
-
return this.i18n.t(key, this.i18nNamespace);
|
|
49447
|
-
}
|
|
49448
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeysModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
49449
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ApiKeysModalComponent, isStandalone: true, selector: "val-api-keys-modal", inputs: { _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
|
|
49450
|
-
<ion-header>
|
|
49451
|
-
<ion-toolbar>
|
|
49452
|
-
<ion-buttons slot="end">
|
|
49453
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49454
|
-
<strong>{{ t('close') }}</strong>
|
|
49455
|
-
</ion-button>
|
|
49456
|
-
</ion-buttons>
|
|
49457
|
-
</ion-toolbar>
|
|
49458
|
-
</ion-header>
|
|
49459
|
-
|
|
49460
|
-
<ion-content class="ion-padding">
|
|
49461
|
-
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
49462
|
-
<val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
|
|
49463
|
-
|
|
49464
|
-
@if (secret(); as s) {
|
|
49465
|
-
<!-- Secreto recién creado: mostrar UNA vez -->
|
|
49466
|
-
<div class="apikey-secret">
|
|
49467
|
-
<val-title [props]="{ content: t('secretTitle'), size: 'large', color: '', bold: true }" />
|
|
49468
|
-
<p class="apikey-secret__warn">{{ t('secretWarning') }}</p>
|
|
49469
|
-
<div class="apikey-secret__box">
|
|
49470
|
-
<code>{{ s.secret }}</code>
|
|
49471
|
-
</div>
|
|
49472
|
-
<div class="apikey-actions">
|
|
49473
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
49474
|
-
{{ copied() ? t('copied') : t('copy') }}
|
|
49475
|
-
</ion-button>
|
|
49476
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
49477
|
-
{{ t('done') }}
|
|
49478
|
-
</ion-button>
|
|
49479
|
-
</div>
|
|
49480
|
-
</div>
|
|
49481
|
-
} @else {
|
|
49482
|
-
@if (loading()) {
|
|
49483
|
-
<ion-spinner name="dots" />
|
|
49484
|
-
} @else {
|
|
49485
|
-
<!-- Lista -->
|
|
49486
|
-
@if (keys().length === 0) {
|
|
49487
|
-
<val-text [props]="{ content: t('empty'), size: 'medium', color: 'medium', bold: false }" />
|
|
49488
|
-
} @else {
|
|
49489
|
-
<div class="apikey-list">
|
|
49490
|
-
@for (k of keys(); track k.keyId) {
|
|
49491
|
-
<div class="apikey-row">
|
|
49492
|
-
<div class="apikey-row__main">
|
|
49493
|
-
<span class="apikey-row__name">{{ k.name }}</span>
|
|
49494
|
-
<span class="apikey-row__meta">
|
|
49495
|
-
{{ k.permissions.length }} {{ t('permsCount') }} · {{ t('lastUsed') }}:
|
|
49496
|
-
{{ k.lastUsedAt || t('never') }}
|
|
49497
|
-
</span>
|
|
49498
|
-
</div>
|
|
49499
|
-
<ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
|
|
49500
|
-
{{ t('revoke') }}
|
|
49501
|
-
</ion-button>
|
|
49502
|
-
</div>
|
|
49503
|
-
}
|
|
49504
|
-
</div>
|
|
49505
|
-
}
|
|
49506
|
-
|
|
49507
|
-
@if (errorMsg()) {
|
|
49508
|
-
<p class="apikey-error">{{ errorMsg() }}</p>
|
|
49509
|
-
}
|
|
49510
|
-
|
|
49511
|
-
<!-- Crear -->
|
|
49512
|
-
@if (showCreate()) {
|
|
49513
|
-
<div class="apikey-create">
|
|
49514
|
-
<ion-input
|
|
49515
|
-
label="{{ t('nameLabel') }}"
|
|
49516
|
-
labelPlacement="stacked"
|
|
49517
|
-
[placeholder]="t('namePlaceholder')"
|
|
49518
|
-
[(ngModel)]="name"
|
|
49519
|
-
/>
|
|
49520
|
-
<p class="apikey-create__label">{{ t('permissionsLabel') }}</p>
|
|
49521
|
-
<div class="apikey-perms">
|
|
49522
|
-
@for (p of availablePermissions(); track p) {
|
|
49523
|
-
<ion-checkbox
|
|
49524
|
-
labelPlacement="end"
|
|
49525
|
-
[checked]="selectedPerms().has(p)"
|
|
49526
|
-
(ionChange)="togglePerm(p, $event)"
|
|
49527
|
-
>
|
|
49528
|
-
{{ p }}
|
|
49529
|
-
</ion-checkbox>
|
|
49530
|
-
}
|
|
49531
|
-
</div>
|
|
49532
|
-
<ion-input
|
|
49533
|
-
label="{{ t('expiresLabel') }}"
|
|
49534
|
-
labelPlacement="stacked"
|
|
49535
|
-
type="number"
|
|
49536
|
-
[(ngModel)]="expiresInDays"
|
|
49537
|
-
/>
|
|
49538
|
-
<div class="apikey-actions">
|
|
49539
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
49540
|
-
@if (submitting()) {
|
|
49541
|
-
<ion-spinner name="dots" /> {{ t('creating') }}
|
|
49542
|
-
} @else {
|
|
49543
|
-
{{ t('create') }}
|
|
49544
|
-
}
|
|
49545
|
-
</ion-button>
|
|
49546
|
-
<ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
|
|
49547
|
-
{{ t('cancel') }}
|
|
49548
|
-
</ion-button>
|
|
49549
|
-
</div>
|
|
49550
|
-
</div>
|
|
49551
|
-
} @else {
|
|
49552
|
-
<div class="apikey-actions">
|
|
49553
|
-
<ion-button expand="block" color="primary" (click)="openCreate()">
|
|
49554
|
-
{{ t('newKey') }}
|
|
49555
|
-
</ion-button>
|
|
49556
|
-
</div>
|
|
49557
|
-
}
|
|
49558
|
-
}
|
|
49559
|
-
}
|
|
49560
|
-
</ion-content>
|
|
49561
|
-
`, isInline: true, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.apikey-list{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.apikey-row{display:flex;align-items:center;justify-content:space-between;padding:12px;border-radius:10px;border:1.5px solid var(--ion-color-light)}.apikey-row__main{display:flex;flex-direction:column}.apikey-row__name{font-weight:600;color:var(--ion-color-dark)}.apikey-row__meta{font-size:12px;color:var(--ion-color-medium-shade, #6b6e76)}.apikey-create{margin-top:8px;padding:16px;border-radius:12px;background:var(--ion-color-light)}.apikey-create__label{margin:12px 0 4px;font-size:13px;font-weight:600;color:var(--ion-color-dark)}.apikey-perms{display:flex;flex-direction:column;gap:6px;margin-bottom:12px;font-size:13px}.apikey-actions{margin-top:16px;display:flex;flex-direction:column;gap:8px}.apikey-error{margin:8px 0;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.apikey-secret__warn{font-size:.875rem;color:var(--ion-color-warning-shade, #b88600);margin:0 0 12px}.apikey-secret__box{padding:12px;border-radius:8px;background:var(--ion-color-dark);color:#fff;word-break:break-all;font-family:monospace;font-size:13px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { 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"] }] }); }
|
|
49562
|
-
}
|
|
49563
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeysModalComponent, decorators: [{
|
|
49564
|
-
type: Component,
|
|
49565
|
-
args: [{ selector: 'val-api-keys-modal', standalone: true, imports: [
|
|
49566
|
-
FormsModule,
|
|
49567
|
-
IonHeader,
|
|
49568
|
-
IonToolbar,
|
|
49569
|
-
IonButtons,
|
|
49570
|
-
IonButton,
|
|
49571
|
-
IonContent,
|
|
49572
|
-
IonInput,
|
|
49573
|
-
IonCheckbox,
|
|
49574
|
-
IonSpinner,
|
|
49575
|
-
DisplayComponent,
|
|
49576
|
-
TextComponent,
|
|
49577
|
-
TitleComponent,
|
|
49578
|
-
], template: `
|
|
49579
|
-
<ion-header>
|
|
49580
|
-
<ion-toolbar>
|
|
49581
|
-
<ion-buttons slot="end">
|
|
49582
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49583
|
-
<strong>{{ t('close') }}</strong>
|
|
49584
|
-
</ion-button>
|
|
49585
|
-
</ion-buttons>
|
|
49586
|
-
</ion-toolbar>
|
|
49587
|
-
</ion-header>
|
|
49588
|
-
|
|
49589
|
-
<ion-content class="ion-padding">
|
|
49590
|
-
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
49591
|
-
<val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
|
|
49592
|
-
|
|
49593
|
-
@if (secret(); as s) {
|
|
49594
|
-
<!-- Secreto recién creado: mostrar UNA vez -->
|
|
49595
|
-
<div class="apikey-secret">
|
|
49596
|
-
<val-title [props]="{ content: t('secretTitle'), size: 'large', color: '', bold: true }" />
|
|
49597
|
-
<p class="apikey-secret__warn">{{ t('secretWarning') }}</p>
|
|
49598
|
-
<div class="apikey-secret__box">
|
|
49599
|
-
<code>{{ s.secret }}</code>
|
|
49600
|
-
</div>
|
|
49601
|
-
<div class="apikey-actions">
|
|
49602
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
49603
|
-
{{ copied() ? t('copied') : t('copy') }}
|
|
49604
|
-
</ion-button>
|
|
49605
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
49606
|
-
{{ t('done') }}
|
|
49607
|
-
</ion-button>
|
|
49608
|
-
</div>
|
|
49609
|
-
</div>
|
|
49610
|
-
} @else {
|
|
49611
|
-
@if (loading()) {
|
|
49612
|
-
<ion-spinner name="dots" />
|
|
49613
|
-
} @else {
|
|
49614
|
-
<!-- Lista -->
|
|
49615
|
-
@if (keys().length === 0) {
|
|
49616
|
-
<val-text [props]="{ content: t('empty'), size: 'medium', color: 'medium', bold: false }" />
|
|
49617
|
-
} @else {
|
|
49618
|
-
<div class="apikey-list">
|
|
49619
|
-
@for (k of keys(); track k.keyId) {
|
|
49620
|
-
<div class="apikey-row">
|
|
49621
|
-
<div class="apikey-row__main">
|
|
49622
|
-
<span class="apikey-row__name">{{ k.name }}</span>
|
|
49623
|
-
<span class="apikey-row__meta">
|
|
49624
|
-
{{ k.permissions.length }} {{ t('permsCount') }} · {{ t('lastUsed') }}:
|
|
49625
|
-
{{ k.lastUsedAt || t('never') }}
|
|
49626
|
-
</span>
|
|
49627
|
-
</div>
|
|
49628
|
-
<ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
|
|
49629
|
-
{{ t('revoke') }}
|
|
49630
|
-
</ion-button>
|
|
49631
|
-
</div>
|
|
49632
|
-
}
|
|
49633
|
-
</div>
|
|
49634
|
-
}
|
|
49635
|
-
|
|
49636
|
-
@if (errorMsg()) {
|
|
49637
|
-
<p class="apikey-error">{{ errorMsg() }}</p>
|
|
49638
|
-
}
|
|
49639
|
-
|
|
49640
|
-
<!-- Crear -->
|
|
49641
|
-
@if (showCreate()) {
|
|
49642
|
-
<div class="apikey-create">
|
|
49643
|
-
<ion-input
|
|
49644
|
-
label="{{ t('nameLabel') }}"
|
|
49645
|
-
labelPlacement="stacked"
|
|
49646
|
-
[placeholder]="t('namePlaceholder')"
|
|
49647
|
-
[(ngModel)]="name"
|
|
49648
|
-
/>
|
|
49649
|
-
<p class="apikey-create__label">{{ t('permissionsLabel') }}</p>
|
|
49650
|
-
<div class="apikey-perms">
|
|
49651
|
-
@for (p of availablePermissions(); track p) {
|
|
49652
|
-
<ion-checkbox
|
|
49653
|
-
labelPlacement="end"
|
|
49654
|
-
[checked]="selectedPerms().has(p)"
|
|
49655
|
-
(ionChange)="togglePerm(p, $event)"
|
|
49656
|
-
>
|
|
49657
|
-
{{ p }}
|
|
49658
|
-
</ion-checkbox>
|
|
49659
|
-
}
|
|
49660
|
-
</div>
|
|
49661
|
-
<ion-input
|
|
49662
|
-
label="{{ t('expiresLabel') }}"
|
|
49663
|
-
labelPlacement="stacked"
|
|
49664
|
-
type="number"
|
|
49665
|
-
[(ngModel)]="expiresInDays"
|
|
49666
|
-
/>
|
|
49667
|
-
<div class="apikey-actions">
|
|
49668
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
49669
|
-
@if (submitting()) {
|
|
49670
|
-
<ion-spinner name="dots" /> {{ t('creating') }}
|
|
49671
|
-
} @else {
|
|
49672
|
-
{{ t('create') }}
|
|
49673
|
-
}
|
|
49674
|
-
</ion-button>
|
|
49675
|
-
<ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
|
|
49676
|
-
{{ t('cancel') }}
|
|
49677
|
-
</ion-button>
|
|
49678
|
-
</div>
|
|
49679
|
-
</div>
|
|
49680
|
-
} @else {
|
|
49681
|
-
<div class="apikey-actions">
|
|
49682
|
-
<ion-button expand="block" color="primary" (click)="openCreate()">
|
|
49683
|
-
{{ t('newKey') }}
|
|
49684
|
-
</ion-button>
|
|
49685
|
-
</div>
|
|
49686
|
-
}
|
|
49687
|
-
}
|
|
49688
|
-
}
|
|
49689
|
-
</ion-content>
|
|
49690
|
-
`, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.apikey-list{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.apikey-row{display:flex;align-items:center;justify-content:space-between;padding:12px;border-radius:10px;border:1.5px solid var(--ion-color-light)}.apikey-row__main{display:flex;flex-direction:column}.apikey-row__name{font-weight:600;color:var(--ion-color-dark)}.apikey-row__meta{font-size:12px;color:var(--ion-color-medium-shade, #6b6e76)}.apikey-create{margin-top:8px;padding:16px;border-radius:12px;background:var(--ion-color-light)}.apikey-create__label{margin:12px 0 4px;font-size:13px;font-weight:600;color:var(--ion-color-dark)}.apikey-perms{display:flex;flex-direction:column;gap:6px;margin-bottom:12px;font-size:13px}.apikey-actions{margin-top:16px;display:flex;flex-direction:column;gap:8px}.apikey-error{margin:8px 0;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.apikey-secret__warn{font-size:.875rem;color:var(--ion-color-warning-shade, #b88600);margin:0 0 12px}.apikey-secret__box{padding:12px;border-radius:8px;background:var(--ion-color-dark);color:#fff;word-break:break-all;font-family:monospace;font-size:13px}\n"] }]
|
|
49691
|
-
}], ctorParameters: () => [], propDecorators: { _modalRef: [{
|
|
49692
|
-
type: Input
|
|
49693
|
-
}], i18nNamespace: [{
|
|
49694
|
-
type: Input
|
|
49695
|
-
}] } });
|
|
49696
|
-
|
|
49697
48849
|
/**
|
|
49698
48850
|
* Defaults i18n (es/en) embebidos en `val-organization-view`. Auto-registrados en
|
|
49699
48851
|
* el constructor del componente si el consumer no proveyó el namespace
|
|
@@ -49749,16 +48901,6 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
49749
48901
|
inviteCtaTitle: '¿Deseas agregar a alguien?',
|
|
49750
48902
|
inviteCtaHint: 'Invita a usuarios a unirse a tu organización.',
|
|
49751
48903
|
inviteOpen: 'Invitar',
|
|
49752
|
-
importOpen: 'Importar',
|
|
49753
|
-
apiKeysOpen: 'Gestionar',
|
|
49754
|
-
apiKeysSectionTitle: 'Integraciones (API Keys)',
|
|
49755
|
-
apiKeysSectionHint: 'También puedes operar tu organización vía API: crea una Client API Key org-scopeada para integraciones máquina-a-máquina (por ejemplo, dar de alta miembros en masa desde tu propio sistema).',
|
|
49756
|
-
apiKeysMoreInfo: 'Más información',
|
|
49757
|
-
orgsMoreInfo: 'Más información',
|
|
49758
|
-
orgsInfoTitle: '¿Qué son las organizaciones?',
|
|
49759
|
-
orgsInfoSubtitle: 'Todo lo que necesitas saber',
|
|
49760
|
-
orgsInfoClose: 'Cerrar',
|
|
49761
|
-
orgsInfoContent: 'Las organizaciones te permiten separar tus proyectos y datos por empresa, equipo o contexto. Puedes pertenecer a varias organizaciones y cambiar entre ellas en cualquier momento.\n\nCada organización tiene sus propios miembros, roles y permisos. La organización activa determina qué datos ves y qué acciones puedes realizar.\n\nPuedes crear una organización nueva o unirte a una existente mediante invitación.',
|
|
49762
48904
|
viewMemberCta: 'Ver miembro',
|
|
49763
48905
|
roleViewer: 'Visor',
|
|
49764
48906
|
roleEditor: 'Editor',
|
|
@@ -49828,16 +48970,6 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
49828
48970
|
inviteCtaTitle: 'Want to add someone?',
|
|
49829
48971
|
inviteCtaHint: 'Invite users to join your organization.',
|
|
49830
48972
|
inviteOpen: 'Invite',
|
|
49831
|
-
importOpen: 'Import',
|
|
49832
|
-
apiKeysOpen: 'Manage',
|
|
49833
|
-
apiKeysSectionTitle: 'Integrations (API Keys)',
|
|
49834
|
-
apiKeysSectionHint: 'You can also operate your organization via API: create an org-scoped Client API Key for machine-to-machine integrations (for example, bulk-provisioning members from your own system).',
|
|
49835
|
-
apiKeysMoreInfo: 'More information',
|
|
49836
|
-
orgsMoreInfo: 'More information',
|
|
49837
|
-
orgsInfoTitle: 'What are organizations?',
|
|
49838
|
-
orgsInfoSubtitle: 'Everything you need to know',
|
|
49839
|
-
orgsInfoClose: 'Close',
|
|
49840
|
-
orgsInfoContent: 'Organizations let you separate your projects and data by company, team, or context. You can belong to multiple organizations and switch between them at any time.\n\nEach organization has its own members, roles, and permissions. The active organization determines what data you see and what actions you can take.\n\nYou can create a new organization or join an existing one via invitation.',
|
|
49841
48973
|
viewMemberCta: 'View member',
|
|
49842
48974
|
roleViewer: 'Viewer',
|
|
49843
48975
|
roleEditor: 'Editor',
|
|
@@ -49862,7 +48994,7 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
49862
48994
|
},
|
|
49863
48995
|
};
|
|
49864
48996
|
|
|
49865
|
-
const DEFAULT_NAMESPACE$
|
|
48997
|
+
const DEFAULT_NAMESPACE$3 = 'Settings.Organization';
|
|
49866
48998
|
const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
|
|
49867
48999
|
const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
|
|
49868
49000
|
/**
|
|
@@ -49919,7 +49051,7 @@ class OrganizationViewComponent {
|
|
|
49919
49051
|
showInvite: merged.showInvite ?? true,
|
|
49920
49052
|
showTransferOwnership: merged.showTransferOwnership ?? true,
|
|
49921
49053
|
showLeave: merged.showLeave ?? true,
|
|
49922
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
49054
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$3,
|
|
49923
49055
|
viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
|
|
49924
49056
|
leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
|
|
49925
49057
|
onOrgEdited: merged.onOrgEdited,
|
|
@@ -49951,22 +49083,6 @@ class OrganizationViewComponent {
|
|
|
49951
49083
|
this.members = signal([]);
|
|
49952
49084
|
this.membersLoading = signal(false);
|
|
49953
49085
|
this.membersLoadError = signal(null);
|
|
49954
|
-
this.membersErrorState = computed(() => {
|
|
49955
|
-
this.i18n.lang();
|
|
49956
|
-
const err = this.membersLoadError();
|
|
49957
|
-
if (!err)
|
|
49958
|
-
return null;
|
|
49959
|
-
return createErrorStateProps(err, {
|
|
49960
|
-
title: { offline: this.tt('offlineTitle'), error: this.tt('errorTitle') },
|
|
49961
|
-
description: {
|
|
49962
|
-
offline: this.tt('offlineHint'),
|
|
49963
|
-
error: interpretError(err).message,
|
|
49964
|
-
},
|
|
49965
|
-
retryLabel: this.tt('retry'),
|
|
49966
|
-
onRetry: () => this.loadMembers(),
|
|
49967
|
-
retrying: this.membersLoading(),
|
|
49968
|
-
});
|
|
49969
|
-
});
|
|
49970
49086
|
this.membersNextToken = signal(null);
|
|
49971
49087
|
this.loadingMoreMembers = signal(false);
|
|
49972
49088
|
this.showAllMembers = signal(false);
|
|
@@ -50053,22 +49169,6 @@ class OrganizationViewComponent {
|
|
|
50053
49169
|
shape: 'round',
|
|
50054
49170
|
type: 'button',
|
|
50055
49171
|
}));
|
|
50056
|
-
this.openImportButtonProps = computed(() => ({
|
|
50057
|
-
text: this.tt('importOpen'),
|
|
50058
|
-
color: 'dark',
|
|
50059
|
-
fill: 'outline',
|
|
50060
|
-
size: 'default',
|
|
50061
|
-
shape: 'round',
|
|
50062
|
-
type: 'button',
|
|
50063
|
-
}));
|
|
50064
|
-
this.openApiKeysButtonProps = computed(() => ({
|
|
50065
|
-
text: this.tt('apiKeysOpen'),
|
|
50066
|
-
color: 'dark',
|
|
50067
|
-
fill: 'clear',
|
|
50068
|
-
size: 'default',
|
|
50069
|
-
shape: 'round',
|
|
50070
|
-
type: 'button',
|
|
50071
|
-
}));
|
|
50072
49172
|
/** Guarda para detectar el primer disparo del effect (evita doble carga). */
|
|
50073
49173
|
this.lastLoadedOrgId = null;
|
|
50074
49174
|
const ns = this.ns;
|
|
@@ -50169,59 +49269,6 @@ class OrganizationViewComponent {
|
|
|
50169
49269
|
backdropDismiss: true,
|
|
50170
49270
|
});
|
|
50171
49271
|
}
|
|
50172
|
-
onOpenImport() {
|
|
50173
|
-
void this.modalService.open({
|
|
50174
|
-
component: MemberImportModalComponent,
|
|
50175
|
-
componentProps: {
|
|
50176
|
-
orgId: this.activeOrgId(),
|
|
50177
|
-
availableRoles: this.availableRoles().map(r => r.name),
|
|
50178
|
-
onSuccess: () => {
|
|
50179
|
-
this.loadMembers();
|
|
50180
|
-
this.resolvedConfig().onMemberInvited?.();
|
|
50181
|
-
},
|
|
50182
|
-
},
|
|
50183
|
-
breakpoints: {
|
|
50184
|
-
breakpoints: [0, 0.92],
|
|
50185
|
-
initialBreakpoint: 0.92,
|
|
50186
|
-
showHandle: true,
|
|
50187
|
-
},
|
|
50188
|
-
backdropDismiss: true,
|
|
50189
|
-
});
|
|
50190
|
-
}
|
|
50191
|
-
onOpenApiKeys() {
|
|
50192
|
-
void this.modalService.open({
|
|
50193
|
-
component: ApiKeysModalComponent,
|
|
50194
|
-
breakpoints: {
|
|
50195
|
-
breakpoints: [0, 0.92],
|
|
50196
|
-
initialBreakpoint: 0.92,
|
|
50197
|
-
showHandle: true,
|
|
50198
|
-
},
|
|
50199
|
-
backdropDismiss: true,
|
|
50200
|
-
});
|
|
50201
|
-
}
|
|
50202
|
-
// "Más información" sobre organizaciones — mismo sheet explicativo que muestra
|
|
50203
|
-
// account-view sin org, pero disponible siempre (todo user tiene org por defecto).
|
|
50204
|
-
onOrgInfo() {
|
|
50205
|
-
void this.modalService.open({
|
|
50206
|
-
component: OrgInfoSheetComponent,
|
|
50207
|
-
componentProps: {
|
|
50208
|
-
title: this.tt('orgsInfoTitle'),
|
|
50209
|
-
subtitle: this.tt('orgsInfoSubtitle'),
|
|
50210
|
-
body: this.tt('orgsInfoContent'),
|
|
50211
|
-
closeLabel: this.tt('orgsInfoClose'),
|
|
50212
|
-
closeProps: {
|
|
50213
|
-
text: this.tt('orgsInfoClose'),
|
|
50214
|
-
fill: 'outline',
|
|
50215
|
-
color: 'dark',
|
|
50216
|
-
size: 'default',
|
|
50217
|
-
expand: 'block',
|
|
50218
|
-
type: 'button',
|
|
50219
|
-
},
|
|
50220
|
-
},
|
|
50221
|
-
breakpoints: { breakpoints: [0, 0.85], initialBreakpoint: 0.85, showHandle: true },
|
|
50222
|
-
backdropDismiss: true,
|
|
50223
|
-
});
|
|
50224
|
-
}
|
|
50225
49272
|
onViewPermissions() {
|
|
50226
49273
|
this.nav.navigateByUrl(this.resolvedConfig().viewPermissionsRoute);
|
|
50227
49274
|
}
|
|
@@ -50303,7 +49350,6 @@ class OrganizationViewComponent {
|
|
|
50303
49350
|
if (!orgId)
|
|
50304
49351
|
return Promise.resolve();
|
|
50305
49352
|
this.membersLoading.set(true);
|
|
50306
|
-
this.membersLoadError.set(null);
|
|
50307
49353
|
this.membersNextToken.set(null);
|
|
50308
49354
|
this.showAllMembers.set(false);
|
|
50309
49355
|
return new Promise(resolve => {
|
|
@@ -50467,12 +49513,6 @@ class OrganizationViewComponent {
|
|
|
50467
49513
|
}
|
|
50468
49514
|
</div>
|
|
50469
49515
|
|
|
50470
|
-
<!-- "Más información" sobre organizaciones — disponible siempre (todo
|
|
50471
|
-
user tiene org por defecto y no ve el empty-state de account). -->
|
|
50472
|
-
<button class="org-more-info-link" type="button" (click)="onOrgInfo()">
|
|
50473
|
-
{{ tt('orgsMoreInfo') }}
|
|
50474
|
-
</button>
|
|
50475
|
-
|
|
50476
49516
|
<div class="section-body">
|
|
50477
49517
|
@if (loading()) {
|
|
50478
49518
|
<val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
|
|
@@ -50520,8 +49560,6 @@ class OrganizationViewComponent {
|
|
|
50520
49560
|
<div class="section-body">
|
|
50521
49561
|
@if (membersLoading()) {
|
|
50522
49562
|
<val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
|
|
50523
|
-
} @else if (membersErrorState()) {
|
|
50524
|
-
<val-empty-state [props]="membersErrorState()!" />
|
|
50525
49563
|
} @else if (members().length === 0) {
|
|
50526
49564
|
<val-text
|
|
50527
49565
|
[props]="{
|
|
@@ -50614,25 +49652,10 @@ class OrganizationViewComponent {
|
|
|
50614
49652
|
/>
|
|
50615
49653
|
</div>
|
|
50616
49654
|
<val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
|
|
50617
|
-
<val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
|
|
50618
49655
|
</div>
|
|
50619
49656
|
</section>
|
|
50620
49657
|
}
|
|
50621
49658
|
|
|
50622
|
-
<!-- API Keys: sección propia. Integraciones M2M (import de miembros, etc.). -->
|
|
50623
|
-
<section *valHasPermission="'api-keys:read'" class="settings-section" data-testid="org-apikeys-section">
|
|
50624
|
-
<div class="invite-cta">
|
|
50625
|
-
<div class="invite-cta__text">
|
|
50626
|
-
<val-title [props]="{ size: 'medium', color: 'dark', bold: true, content: tt('apiKeysSectionTitle') }" />
|
|
50627
|
-
<val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: tt('apiKeysSectionHint') }" />
|
|
50628
|
-
<button class="org-more-info-link" type="button" (click)="onOpenApiKeys()">
|
|
50629
|
-
{{ tt('apiKeysMoreInfo') }}
|
|
50630
|
-
</button>
|
|
50631
|
-
</div>
|
|
50632
|
-
<val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
|
|
50633
|
-
</div>
|
|
50634
|
-
</section>
|
|
50635
|
-
|
|
50636
49659
|
@if (resolvedConfig().showTransferOwnership && isOwner()) {
|
|
50637
49660
|
<!-- Transfer ownership: solo owner (gating RBAC interno) -->
|
|
50638
49661
|
<section class="settings-section" data-testid="org-transfer-section">
|
|
@@ -50723,7 +49746,7 @@ class OrganizationViewComponent {
|
|
|
50723
49746
|
</section>
|
|
50724
49747
|
}
|
|
50725
49748
|
</div>
|
|
50726
|
-
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.
|
|
49749
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap}.invite-cta__text{display:flex;flex-direction:column;gap:4px;flex:1}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field--full{width:100%}.org-info-field:last-child{border-bottom:none}.org-info-row{display:flex}.org-info-row .org-info-field{flex:1}.org-info-row .org-info-field:first-child{border-right:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "directive", type: HasPermissionDirective, selector: "[valHasPermission]", inputs: ["valHasPermission"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: MemberCardComponent, selector: "val-member-card", inputs: ["props"], outputs: ["onAction"] }] }); }
|
|
50727
49750
|
}
|
|
50728
49751
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrganizationViewComponent, decorators: [{
|
|
50729
49752
|
type: Component,
|
|
@@ -50737,9 +49760,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50737
49760
|
ButtonComponent,
|
|
50738
49761
|
MemberCardComponent,
|
|
50739
49762
|
TransferOwnershipModalComponent,
|
|
50740
|
-
MemberImportModalComponent,
|
|
50741
|
-
ApiKeysModalComponent,
|
|
50742
|
-
OrgInfoSheetComponent,
|
|
50743
49763
|
], template: `
|
|
50744
49764
|
<div class="page">
|
|
50745
49765
|
<header class="page-header">
|
|
@@ -50771,12 +49791,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50771
49791
|
}
|
|
50772
49792
|
</div>
|
|
50773
49793
|
|
|
50774
|
-
<!-- "Más información" sobre organizaciones — disponible siempre (todo
|
|
50775
|
-
user tiene org por defecto y no ve el empty-state de account). -->
|
|
50776
|
-
<button class="org-more-info-link" type="button" (click)="onOrgInfo()">
|
|
50777
|
-
{{ tt('orgsMoreInfo') }}
|
|
50778
|
-
</button>
|
|
50779
|
-
|
|
50780
49794
|
<div class="section-body">
|
|
50781
49795
|
@if (loading()) {
|
|
50782
49796
|
<val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
|
|
@@ -50824,8 +49838,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50824
49838
|
<div class="section-body">
|
|
50825
49839
|
@if (membersLoading()) {
|
|
50826
49840
|
<val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
|
|
50827
|
-
} @else if (membersErrorState()) {
|
|
50828
|
-
<val-empty-state [props]="membersErrorState()!" />
|
|
50829
49841
|
} @else if (members().length === 0) {
|
|
50830
49842
|
<val-text
|
|
50831
49843
|
[props]="{
|
|
@@ -50918,25 +49930,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50918
49930
|
/>
|
|
50919
49931
|
</div>
|
|
50920
49932
|
<val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
|
|
50921
|
-
<val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
|
|
50922
49933
|
</div>
|
|
50923
49934
|
</section>
|
|
50924
49935
|
}
|
|
50925
49936
|
|
|
50926
|
-
<!-- API Keys: sección propia. Integraciones M2M (import de miembros, etc.). -->
|
|
50927
|
-
<section *valHasPermission="'api-keys:read'" class="settings-section" data-testid="org-apikeys-section">
|
|
50928
|
-
<div class="invite-cta">
|
|
50929
|
-
<div class="invite-cta__text">
|
|
50930
|
-
<val-title [props]="{ size: 'medium', color: 'dark', bold: true, content: tt('apiKeysSectionTitle') }" />
|
|
50931
|
-
<val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: tt('apiKeysSectionHint') }" />
|
|
50932
|
-
<button class="org-more-info-link" type="button" (click)="onOpenApiKeys()">
|
|
50933
|
-
{{ tt('apiKeysMoreInfo') }}
|
|
50934
|
-
</button>
|
|
50935
|
-
</div>
|
|
50936
|
-
<val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
|
|
50937
|
-
</div>
|
|
50938
|
-
</section>
|
|
50939
|
-
|
|
50940
49937
|
@if (resolvedConfig().showTransferOwnership && isOwner()) {
|
|
50941
49938
|
<!-- Transfer ownership: solo owner (gating RBAC interno) -->
|
|
50942
49939
|
<section class="settings-section" data-testid="org-transfer-section">
|
|
@@ -51027,7 +50024,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
51027
50024
|
</section>
|
|
51028
50025
|
}
|
|
51029
50026
|
</div>
|
|
51030
|
-
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.
|
|
50027
|
+
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap}.invite-cta__text{display:flex;flex-direction:column;gap:4px;flex:1}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field--full{width:100%}.org-info-field:last-child{border-bottom:none}.org-info-row{display:flex}.org-info-row .org-info-field{flex:1}.org-info-row .org-info-field:first-child{border-right:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}\n"] }]
|
|
51031
50028
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
51032
50029
|
type: Input
|
|
51033
50030
|
}] } });
|
|
@@ -51141,7 +50138,7 @@ addIcons({
|
|
|
51141
50138
|
notificationsOutline,
|
|
51142
50139
|
refreshOutline,
|
|
51143
50140
|
});
|
|
51144
|
-
const DEFAULT_NAMESPACE$
|
|
50141
|
+
const DEFAULT_NAMESPACE$2 = 'Settings.Notifications';
|
|
51145
50142
|
/**
|
|
51146
50143
|
* `val-notification-preferences-view` — **preferencias de notificaciones push**
|
|
51147
50144
|
* (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
|
|
@@ -51184,7 +50181,7 @@ class NotificationPreferencesViewComponent {
|
|
|
51184
50181
|
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
51185
50182
|
return {
|
|
51186
50183
|
showDetails: merged.showDetails ?? true,
|
|
51187
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
50184
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
|
|
51188
50185
|
onPushEnabled: merged.onPushEnabled,
|
|
51189
50186
|
onPushDisabled: merged.onPushDisabled,
|
|
51190
50187
|
};
|
|
@@ -51955,7 +50952,7 @@ const PERMISSIONS_VIEW_I18N = {
|
|
|
51955
50952
|
},
|
|
51956
50953
|
};
|
|
51957
50954
|
|
|
51958
|
-
const DEFAULT_NAMESPACE$
|
|
50955
|
+
const DEFAULT_NAMESPACE$1 = 'Settings.Permissions';
|
|
51959
50956
|
/**
|
|
51960
50957
|
* `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
|
|
51961
50958
|
* Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
|
|
@@ -51995,7 +50992,7 @@ class PermissionsViewComponent {
|
|
|
51995
50992
|
return {
|
|
51996
50993
|
showSystemRoles: merged.showSystemRoles ?? true,
|
|
51997
50994
|
showCustomRoles: merged.showCustomRoles ?? true,
|
|
51998
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
50995
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
|
|
51999
50996
|
};
|
|
52000
50997
|
});
|
|
52001
50998
|
this.loading = signal(false);
|
|
@@ -52772,7 +51769,7 @@ addIcons({
|
|
|
52772
51769
|
openOutline,
|
|
52773
51770
|
personOutline,
|
|
52774
51771
|
});
|
|
52775
|
-
const DEFAULT_NAMESPACE
|
|
51772
|
+
const DEFAULT_NAMESPACE = 'Notifications';
|
|
52776
51773
|
const DEFAULT_SETTINGS_PATH = '/app/settings/notifications';
|
|
52777
51774
|
const DEFAULT_PAGE_SIZE = 10;
|
|
52778
51775
|
const DEFAULT_MAX_LOAD = 50;
|
|
@@ -52832,7 +51829,7 @@ class NotificationsViewComponent {
|
|
|
52832
51829
|
showPushOffCta: merged.showPushOffCta ?? true,
|
|
52833
51830
|
enableMarkAll: merged.enableMarkAll ?? true,
|
|
52834
51831
|
settingsNotificationsPath: merged.settingsNotificationsPath ?? DEFAULT_SETTINGS_PATH,
|
|
52835
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE
|
|
51832
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
|
|
52836
51833
|
pageSize: merged.pageSize ?? DEFAULT_PAGE_SIZE,
|
|
52837
51834
|
maxLoad: merged.maxLoad ?? DEFAULT_MAX_LOAD,
|
|
52838
51835
|
onNotificationOpen: merged.onNotificationOpen,
|
|
@@ -53500,797 +52497,6 @@ function provideValtechNotificationsRoutes(opts) {
|
|
|
53500
52497
|
];
|
|
53501
52498
|
}
|
|
53502
52499
|
|
|
53503
|
-
/**
|
|
53504
|
-
* Defaults i18n del componente (es/en). El componente los auto-registra en su
|
|
53505
|
-
* constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
|
|
53506
|
-
* garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
|
|
53507
|
-
* template por cada change-detection → `console.warn` por CD → con el
|
|
53508
|
-
* `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
|
|
53509
|
-
* infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
|
|
53510
|
-
*/
|
|
53511
|
-
const CONTENT_REACTION_I18N = {
|
|
53512
|
-
es: {
|
|
53513
|
-
question: '¿Te resultó útil este contenido?',
|
|
53514
|
-
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
53515
|
-
submit: 'Enviar opinión',
|
|
53516
|
-
update: 'Actualizar opinión',
|
|
53517
|
-
thankYou: '¡Gracias por tu opinión!',
|
|
53518
|
-
submitted: 'Tu opinión ha sido registrada',
|
|
53519
|
-
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
53520
|
-
positiveLabel: 'Sí, mucho',
|
|
53521
|
-
negativeLabel: 'No tanto',
|
|
53522
|
-
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
53523
|
-
},
|
|
53524
|
-
en: {
|
|
53525
|
-
question: 'Was this content helpful?',
|
|
53526
|
-
commentPlaceholder: 'Tell us more (optional)...',
|
|
53527
|
-
submit: 'Send feedback',
|
|
53528
|
-
update: 'Update feedback',
|
|
53529
|
-
thankYou: 'Thanks for your feedback!',
|
|
53530
|
-
submitted: 'Your feedback has been recorded',
|
|
53531
|
-
errorSubmitting: 'Failed to send. Please try again.',
|
|
53532
|
-
positiveLabel: 'Yes, a lot',
|
|
53533
|
-
negativeLabel: 'Not really',
|
|
53534
|
-
negativeFeedbackTitle: "Tell us what you didn't like?",
|
|
53535
|
-
},
|
|
53536
|
-
};
|
|
53537
|
-
/**
|
|
53538
|
-
* Componente para reacciones de contenido con emojis.
|
|
53539
|
-
*
|
|
53540
|
-
* @example
|
|
53541
|
-
* ```html
|
|
53542
|
-
* <val-content-reaction
|
|
53543
|
-
* [props]="{
|
|
53544
|
-
* entityRef: { entityType: 'article', entityId: 'art-123' },
|
|
53545
|
-
* question: '¿Te fue útil este artículo?'
|
|
53546
|
-
* }"
|
|
53547
|
-
* (reactionSubmit)="onReactionSubmit($event)"
|
|
53548
|
-
* />
|
|
53549
|
-
* ```
|
|
53550
|
-
*/
|
|
53551
|
-
class ContentReactionComponent {
|
|
53552
|
-
constructor() {
|
|
53553
|
-
this.feedbackService = inject(FeedbackService);
|
|
53554
|
-
this.toast = inject(ToastService);
|
|
53555
|
-
this.i18n = inject(I18nService);
|
|
53556
|
-
this.auth = inject(AuthService, { optional: true });
|
|
53557
|
-
this.isConfigured = computed(() => this.feedbackService.isConfigured());
|
|
53558
|
-
this.props = {};
|
|
53559
|
-
this.reactionSubmit = new EventEmitter();
|
|
53560
|
-
this.reactionChange = new EventEmitter();
|
|
53561
|
-
// Estado reactivo
|
|
53562
|
-
this.state = signal({
|
|
53563
|
-
selectedValue: null,
|
|
53564
|
-
comment: '',
|
|
53565
|
-
isLoading: false,
|
|
53566
|
-
isSubmitted: false,
|
|
53567
|
-
hadPreviousReaction: false,
|
|
53568
|
-
error: null,
|
|
53569
|
-
});
|
|
53570
|
-
// Valores por defecto
|
|
53571
|
-
this.defaultEmojis = ['😞', '😐', '😊'];
|
|
53572
|
-
this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
|
|
53573
|
-
// Computed properties
|
|
53574
|
-
this.resolvedProps = computed(() => ({
|
|
53575
|
-
entityRef: this.props.entityRef,
|
|
53576
|
-
question: this.props.question || this.t('question'),
|
|
53577
|
-
showComment: this.props.showComment ?? true,
|
|
53578
|
-
commentOnValues: this.props.commentOnValues,
|
|
53579
|
-
commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
|
|
53580
|
-
maxCommentLength: this.props.maxCommentLength ?? 500,
|
|
53581
|
-
emojis: this.props.emojis || this.defaultEmojis,
|
|
53582
|
-
emojiLabels: this.props.emojiLabels || this.defaultLabels,
|
|
53583
|
-
showThankYou: this.props.showThankYou ?? true,
|
|
53584
|
-
thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
|
|
53585
|
-
disabled: this.props.disabled ?? false,
|
|
53586
|
-
readonly: this.props.readonly ?? false,
|
|
53587
|
-
// Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
|
|
53588
|
-
variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
|
|
53589
|
-
}));
|
|
53590
|
-
this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
|
|
53591
|
-
this.showCommentField = computed(() => {
|
|
53592
|
-
const sel = this.state().selectedValue;
|
|
53593
|
-
if (!sel)
|
|
53594
|
-
return false;
|
|
53595
|
-
const commentOn = this.resolvedProps().commentOnValues;
|
|
53596
|
-
if (commentOn !== undefined)
|
|
53597
|
-
return commentOn.includes(sel);
|
|
53598
|
-
return this.resolvedProps().showComment;
|
|
53599
|
-
});
|
|
53600
|
-
this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
|
|
53601
|
-
// Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
|
|
53602
|
-
// namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
|
|
53603
|
-
// CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
|
|
53604
|
-
if (!this.i18n.hasNamespace('ContentReaction')) {
|
|
53605
|
-
this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
|
|
53606
|
-
}
|
|
53607
|
-
}
|
|
53608
|
-
ngOnInit() {
|
|
53609
|
-
this.loadPreviousReaction();
|
|
53610
|
-
}
|
|
53611
|
-
ngOnChanges(changes) {
|
|
53612
|
-
if (changes['props'] && !changes['props'].firstChange) {
|
|
53613
|
-
this.loadPreviousReaction();
|
|
53614
|
-
}
|
|
53615
|
-
}
|
|
53616
|
-
async loadPreviousReaction() {
|
|
53617
|
-
if (!this.props.entityRef)
|
|
53618
|
-
return;
|
|
53619
|
-
// Si hay initialValue, usarlo directamente sin consultar
|
|
53620
|
-
if (this.props.initialValue) {
|
|
53621
|
-
this.state.update(s => ({
|
|
53622
|
-
...s,
|
|
53623
|
-
selectedValue: this.props.initialValue,
|
|
53624
|
-
hadPreviousReaction: true,
|
|
53625
|
-
isLoading: false,
|
|
53626
|
-
isSubmitted: true,
|
|
53627
|
-
}));
|
|
53628
|
-
return;
|
|
53629
|
-
}
|
|
53630
|
-
// Si skipInitialCheck está activo, no consultar
|
|
53631
|
-
if (this.props.skipInitialCheck) {
|
|
53632
|
-
return;
|
|
53633
|
-
}
|
|
53634
|
-
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
53635
|
-
try {
|
|
53636
|
-
const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
|
|
53637
|
-
if (check.hasFeedback && check.reactionValue) {
|
|
53638
|
-
this.state.update(s => ({
|
|
53639
|
-
...s,
|
|
53640
|
-
selectedValue: check.reactionValue,
|
|
53641
|
-
hadPreviousReaction: true,
|
|
53642
|
-
isLoading: false,
|
|
53643
|
-
isSubmitted: true,
|
|
53644
|
-
}));
|
|
53645
|
-
}
|
|
53646
|
-
else {
|
|
53647
|
-
this.state.update(s => ({ ...s, isLoading: false }));
|
|
53648
|
-
}
|
|
53649
|
-
}
|
|
53650
|
-
catch (error) {
|
|
53651
|
-
console.error('Error loading previous reaction:', error);
|
|
53652
|
-
this.state.update(s => ({ ...s, isLoading: false }));
|
|
53653
|
-
}
|
|
53654
|
-
}
|
|
53655
|
-
selectReaction(value) {
|
|
53656
|
-
if (this.resolvedProps().disabled || this.resolvedProps().readonly)
|
|
53657
|
-
return;
|
|
53658
|
-
const previousValue = this.state().selectedValue;
|
|
53659
|
-
this.state.update(s => ({
|
|
53660
|
-
...s,
|
|
53661
|
-
selectedValue: value,
|
|
53662
|
-
isSubmitted: false,
|
|
53663
|
-
error: null,
|
|
53664
|
-
}));
|
|
53665
|
-
this.reactionChange.emit({ value, previousValue });
|
|
53666
|
-
}
|
|
53667
|
-
async submitReaction() {
|
|
53668
|
-
const currentState = this.state();
|
|
53669
|
-
const props = this.resolvedProps();
|
|
53670
|
-
if (!currentState.selectedValue || props.disabled)
|
|
53671
|
-
return;
|
|
53672
|
-
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
53673
|
-
try {
|
|
53674
|
-
// Determinar si usar endpoint anónimo o autenticado
|
|
53675
|
-
const isAuthenticated = this.auth?.isAuthenticated() ?? false;
|
|
53676
|
-
const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
|
|
53677
|
-
if (useAnonymous) {
|
|
53678
|
-
// Usar endpoint anónimo (público)
|
|
53679
|
-
await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
53680
|
-
}
|
|
53681
|
-
else {
|
|
53682
|
-
// Usar endpoint autenticado
|
|
53683
|
-
await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
53684
|
-
}
|
|
53685
|
-
this.state.update(s => ({
|
|
53686
|
-
...s,
|
|
53687
|
-
isLoading: false,
|
|
53688
|
-
isSubmitted: true,
|
|
53689
|
-
comment: '',
|
|
53690
|
-
hadPreviousReaction: !useAnonymous, // Solo para autenticados
|
|
53691
|
-
}));
|
|
53692
|
-
this.reactionSubmit.emit({
|
|
53693
|
-
value: currentState.selectedValue,
|
|
53694
|
-
comment: currentState.comment || undefined,
|
|
53695
|
-
entityRef: props.entityRef,
|
|
53696
|
-
isUpdate: currentState.hadPreviousReaction,
|
|
53697
|
-
});
|
|
53698
|
-
if (props.showThankYou) {
|
|
53699
|
-
this.toast.show({
|
|
53700
|
-
message: props.thankYouMessage,
|
|
53701
|
-
duration: 2000,
|
|
53702
|
-
});
|
|
53703
|
-
}
|
|
53704
|
-
}
|
|
53705
|
-
catch (error) {
|
|
53706
|
-
console.error('Error submitting reaction:', error);
|
|
53707
|
-
this.state.update(s => ({
|
|
53708
|
-
...s,
|
|
53709
|
-
isLoading: false,
|
|
53710
|
-
error: this.t('errorSubmitting'),
|
|
53711
|
-
}));
|
|
53712
|
-
this.toast.show({
|
|
53713
|
-
message: this.t('errorSubmitting'),
|
|
53714
|
-
duration: 3000,
|
|
53715
|
-
});
|
|
53716
|
-
}
|
|
53717
|
-
}
|
|
53718
|
-
updateComment(event) {
|
|
53719
|
-
const value = event.detail.value || '';
|
|
53720
|
-
this.state.update(s => ({ ...s, comment: value }));
|
|
53721
|
-
}
|
|
53722
|
-
getEmoji(index) {
|
|
53723
|
-
return this.resolvedProps().emojis[index];
|
|
53724
|
-
}
|
|
53725
|
-
getEmojiLabel(index) {
|
|
53726
|
-
return this.resolvedProps().emojiLabels[index];
|
|
53727
|
-
}
|
|
53728
|
-
isSelected(value) {
|
|
53729
|
-
return this.state().selectedValue === value;
|
|
53730
|
-
}
|
|
53731
|
-
t(key) {
|
|
53732
|
-
const translations = {
|
|
53733
|
-
question: '¿Te resultó útil este contenido?',
|
|
53734
|
-
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
53735
|
-
submit: 'Enviar opinión',
|
|
53736
|
-
update: 'Actualizar opinión',
|
|
53737
|
-
thankYou: '¡Gracias por tu opinión!',
|
|
53738
|
-
submitted: 'Tu opinión ha sido registrada',
|
|
53739
|
-
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
53740
|
-
positiveLabel: 'Sí, mucho',
|
|
53741
|
-
negativeLabel: 'No tanto',
|
|
53742
|
-
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
53743
|
-
};
|
|
53744
|
-
return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
|
|
53745
|
-
}
|
|
53746
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
53747
|
-
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"] }] }); }
|
|
53748
|
-
}
|
|
53749
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
|
|
53750
|
-
type: Component,
|
|
53751
|
-
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"] }]
|
|
53752
|
-
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
53753
|
-
type: Input
|
|
53754
|
-
}], reactionSubmit: [{
|
|
53755
|
-
type: Output
|
|
53756
|
-
}], reactionChange: [{
|
|
53757
|
-
type: Output
|
|
53758
|
-
}] } });
|
|
53759
|
-
|
|
53760
|
-
/**
|
|
53761
|
-
* Defaults i18n (es/en) embebidos en `val-about-view`. Auto-registrados en el
|
|
53762
|
-
* constructor del componente si el consumer no proveyó el namespace
|
|
53763
|
-
* (`Settings.About` por default). SOLO incluye las keys que la vista usa.
|
|
53764
|
-
*
|
|
53765
|
-
* Textos portados EXACTOS del override `Settings.About` de showcase
|
|
53766
|
-
* (`frontend/showcase/src/i18n/index.ts`). Las keys de las acciones
|
|
53767
|
-
* showcase-specific (`feedback*`, `share*`, `faq*`, `donate*`) NO se incluyen:
|
|
53768
|
-
* esas acciones se modelan ahora como `actions[]` de la config (el consumer
|
|
53769
|
-
* aporta sus propios textos). `copyrightText` se incluye aquí (antes salía del
|
|
53770
|
-
* namespace `Layout`) para que la vista sea autocontenida.
|
|
53771
|
-
*/
|
|
53772
|
-
const ABOUT_VIEW_I18N = {
|
|
53773
|
-
es: {
|
|
53774
|
-
pageTitle: 'Acerca de',
|
|
53775
|
-
pageDescription: 'Demo del factory Valtech',
|
|
53776
|
-
tagline: 'App de demo — explora las funcionalidades en vivo',
|
|
53777
|
-
descTitle: 'Acerca de Showcase',
|
|
53778
|
-
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.',
|
|
53779
|
-
actionsTitle: 'Acciones',
|
|
53780
|
-
rateTitle: 'Calificar la app',
|
|
53781
|
-
rateDesc: 'Cuéntanos qué te ha parecido la experiencia.',
|
|
53782
|
-
rateModalTitle: 'Tu opinión',
|
|
53783
|
-
rateQuestion: '¿Qué te ha parecido la experiencia hasta ahora?',
|
|
53784
|
-
rateComment: 'Cuéntanos más (opcional)…',
|
|
53785
|
-
rateThanks: '¡Gracias por tu opinión!',
|
|
53786
|
-
rateClose: 'Cerrar',
|
|
53787
|
-
rateBad: 'No tanto',
|
|
53788
|
-
rateMid: 'Bien',
|
|
53789
|
-
rateGood: '¡Me encanta!',
|
|
53790
|
-
socialTitle: 'Síguenos',
|
|
53791
|
-
socialDesc: 'Estamos en redes sociales.',
|
|
53792
|
-
versionLabel: 'Versión',
|
|
53793
|
-
versionCopied: 'v{v} copiado',
|
|
53794
|
-
copyrightText: '© {year}, Soluciones Valtech Ltda. o sus afiliados. Todos los derechos reservados.',
|
|
53795
|
-
},
|
|
53796
|
-
en: {
|
|
53797
|
-
pageTitle: 'About',
|
|
53798
|
-
pageDescription: 'Valtech factory demo',
|
|
53799
|
-
tagline: 'Demo app — explore features live',
|
|
53800
|
-
descTitle: 'About Showcase',
|
|
53801
|
-
descBody: 'Showcase is the reference app for the Valtech factory. Explore the full flow live: authentication, profile, organizations, notifications and account settings.',
|
|
53802
|
-
actionsTitle: 'Actions',
|
|
53803
|
-
rateTitle: 'Rate the app',
|
|
53804
|
-
rateDesc: 'Tell us how your experience has been.',
|
|
53805
|
-
rateModalTitle: 'Your feedback',
|
|
53806
|
-
rateQuestion: 'How has your experience been so far?',
|
|
53807
|
-
rateComment: 'Tell us more (optional)…',
|
|
53808
|
-
rateThanks: 'Thanks for your feedback!',
|
|
53809
|
-
rateClose: 'Close',
|
|
53810
|
-
rateBad: 'Not great',
|
|
53811
|
-
rateMid: 'Good',
|
|
53812
|
-
rateGood: 'Love it!',
|
|
53813
|
-
socialTitle: 'Follow us',
|
|
53814
|
-
socialDesc: "We're on social media.",
|
|
53815
|
-
versionLabel: 'Version',
|
|
53816
|
-
versionCopied: 'v{v} copied',
|
|
53817
|
-
copyrightText: '© {year}, Soluciones Valtech Ltda. or its affiliates. All rights reserved.',
|
|
53818
|
-
},
|
|
53819
|
-
};
|
|
53820
|
-
|
|
53821
|
-
addIcons({
|
|
53822
|
-
copyOutline,
|
|
53823
|
-
starOutline,
|
|
53824
|
-
chatbubbleEllipsesOutline,
|
|
53825
|
-
shareSocialOutline,
|
|
53826
|
-
helpCircleOutline,
|
|
53827
|
-
logoFacebook,
|
|
53828
|
-
logoInstagram,
|
|
53829
|
-
logoLinkedin,
|
|
53830
|
-
logoTwitter,
|
|
53831
|
-
logoYoutube,
|
|
53832
|
-
logoTiktok,
|
|
53833
|
-
});
|
|
53834
|
-
const DEFAULT_NAMESPACE = 'Settings.About';
|
|
53835
|
-
const DEFAULT_BRAND_NAME = 'Valtech';
|
|
53836
|
-
const DEFAULT_LOGO_SRC = 'assets/images/main-icon.png';
|
|
53837
|
-
const DEFAULT_RATING_ENTITY_ID = 'experience';
|
|
53838
|
-
/**
|
|
53839
|
-
* `val-about-view` — vista About full-feature autocontenida (organism).
|
|
53840
|
-
* Promovida desde `showcase` bajo el proceso de ADR-021. Reúne:
|
|
53841
|
-
* - **HERO** — logo + nombre de marca + tagline + pill de versión (copiable).
|
|
53842
|
-
* - **Descripción** — título + cuerpo.
|
|
53843
|
-
* - **Acciones** — action-card de rating built-in (abre `val-content-reaction`
|
|
53844
|
-
* DIRECTAMENTE vía `ModalService`, sin wrapper local — decisión CTO) + las
|
|
53845
|
-
* acciones genéricas que el consumer aporte por `config.actions`.
|
|
53846
|
-
* - **Redes** — links sociales (`config.socialLinks`); sección oculta si vacía.
|
|
53847
|
-
* - **Footer** — copyright con el año actual.
|
|
53848
|
-
*
|
|
53849
|
-
* Lo app-specific (logo, nombre, versión, redes, acciones share/faq) viaja como
|
|
53850
|
-
* DATA de config — NO como slots (ADR-021). La versión la pasa el consumer
|
|
53851
|
-
* (`config.version` = su `APP_VERSION`); la lib NO importa el `version.ts` de la
|
|
53852
|
-
* app. Si no se pasa versión, el pill se oculta.
|
|
53853
|
-
*
|
|
53854
|
-
* NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute`
|
|
53855
|
-
* se inyecta `{ optional: true }` solo para leer el route data de config.
|
|
53856
|
-
*
|
|
53857
|
-
* Auto-registra sus defaults i18n (es/en) en el constructor si el consumer no
|
|
53858
|
-
* proveyó el namespace configurado (default `Settings.About`).
|
|
53859
|
-
*
|
|
53860
|
-
* GOTCHA i18n ContentReaction: `val-content-reaction` exige su namespace
|
|
53861
|
-
* `ContentReaction` registrado donde se use (key faltante en binding + CD =
|
|
53862
|
-
* navegador congelado en dev). `ContentReactionComponent` se auto-registra ese
|
|
53863
|
-
* namespace en SU constructor, y como se abre vía `ModalService` ese constructor
|
|
53864
|
-
* corre antes de que su template binde — queda cubierto. Igual lo importamos
|
|
53865
|
-
* como dependencia explícita de esta vista para que la garantía sea local.
|
|
53866
|
-
*/
|
|
53867
|
-
class AboutViewComponent {
|
|
53868
|
-
/** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
|
|
53869
|
-
get ns() {
|
|
53870
|
-
return this.resolvedConfig().i18nNamespace;
|
|
53871
|
-
}
|
|
53872
|
-
constructor() {
|
|
53873
|
-
this.nav = inject(NavigationService);
|
|
53874
|
-
this.i18n = inject(I18nService);
|
|
53875
|
-
this.modalService = inject(ModalService);
|
|
53876
|
-
this.toast = inject(ToastService);
|
|
53877
|
-
this.route = inject(ActivatedRoute, { optional: true });
|
|
53878
|
-
this.resolvedConfig = computed(() => {
|
|
53879
|
-
const fromRoute = (this.route?.snapshot.data['aboutConfig'] ?? {});
|
|
53880
|
-
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
53881
|
-
return {
|
|
53882
|
-
brand: {
|
|
53883
|
-
name: merged.brand?.name ?? DEFAULT_BRAND_NAME,
|
|
53884
|
-
logoSrc: merged.brand?.logoSrc ?? DEFAULT_LOGO_SRC,
|
|
53885
|
-
},
|
|
53886
|
-
version: merged.version ?? '',
|
|
53887
|
-
socialLinks: merged.socialLinks ?? [],
|
|
53888
|
-
showRating: merged.showRating ?? true,
|
|
53889
|
-
ratingEntityId: merged.ratingEntityId ?? DEFAULT_RATING_ENTITY_ID,
|
|
53890
|
-
actions: merged.actions ?? [],
|
|
53891
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
|
|
53892
|
-
onRated: merged.onRated,
|
|
53893
|
-
};
|
|
53894
|
-
});
|
|
53895
|
-
this.brandName = computed(() => this.resolvedConfig().brand.name);
|
|
53896
|
-
this.logoSrc = computed(() => this.resolvedConfig().brand.logoSrc);
|
|
53897
|
-
this.version = computed(() => this.resolvedConfig().version);
|
|
53898
|
-
this.socialLinks = computed(() => this.resolvedConfig().socialLinks);
|
|
53899
|
-
this.showRating = computed(() => this.resolvedConfig().showRating);
|
|
53900
|
-
this.actions = computed(() => this.resolvedConfig().actions);
|
|
53901
|
-
/** Copyright del footer — año actual interpolado en `copyrightText`. */
|
|
53902
|
-
this.copyright = computed(() => {
|
|
53903
|
-
this.i18n.lang();
|
|
53904
|
-
const year = String(new Date().getFullYear());
|
|
53905
|
-
return this.tt('copyrightText').replace('{year}', year);
|
|
53906
|
-
});
|
|
53907
|
-
/** Title metadata para secciones — bold + size small. */
|
|
53908
|
-
this.rateActionCard = computed(() => {
|
|
53909
|
-
this.i18n.lang();
|
|
53910
|
-
return {
|
|
53911
|
-
icon: { ionicon: 'star-outline', color: 'primary' },
|
|
53912
|
-
title: this.tt('rateTitle'),
|
|
53913
|
-
description: this.tt('rateDesc'),
|
|
53914
|
-
size: 'medium',
|
|
53915
|
-
bordered: false,
|
|
53916
|
-
shadowed: false,
|
|
53917
|
-
};
|
|
53918
|
-
});
|
|
53919
|
-
const ns = this.ns;
|
|
53920
|
-
if (!this.i18n.hasNamespace(ns)) {
|
|
53921
|
-
this.i18n.registerContent(ns, ABOUT_VIEW_I18N);
|
|
53922
|
-
}
|
|
53923
|
-
this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
|
|
53924
|
-
}
|
|
53925
|
-
/** Helper i18n bound al namespace resuelto. Reactivo vía `lang()`. */
|
|
53926
|
-
tt(key) {
|
|
53927
|
-
this.i18n.lang();
|
|
53928
|
-
return this.i18n.t(key, this.ns);
|
|
53929
|
-
}
|
|
53930
|
-
/** Title metadata para secciones — bold + size small. */
|
|
53931
|
-
sectionTitle(key) {
|
|
53932
|
-
this.i18n.lang();
|
|
53933
|
-
return {
|
|
53934
|
-
content: this.tt(key),
|
|
53935
|
-
size: 'small',
|
|
53936
|
-
color: 'dark',
|
|
53937
|
-
bold: true,
|
|
53938
|
-
};
|
|
53939
|
-
}
|
|
53940
|
-
/** ActionCardMetadata para una acción genérica del consumer. */
|
|
53941
|
-
extraActionCard(action) {
|
|
53942
|
-
return {
|
|
53943
|
-
icon: { ionicon: action.icon ?? 'chatbubble-ellipses-outline', color: 'primary' },
|
|
53944
|
-
title: action.label,
|
|
53945
|
-
description: action.description,
|
|
53946
|
-
size: 'medium',
|
|
53947
|
-
bordered: false,
|
|
53948
|
-
shadowed: false,
|
|
53949
|
-
};
|
|
53950
|
-
}
|
|
53951
|
-
/** Copia la versión al portapapeles + toast (color dark). */
|
|
53952
|
-
async copyVersion() {
|
|
53953
|
-
const v = this.version();
|
|
53954
|
-
if (!v)
|
|
53955
|
-
return;
|
|
53956
|
-
try {
|
|
53957
|
-
await navigator.clipboard.writeText(v);
|
|
53958
|
-
this.toast.show({
|
|
53959
|
-
message: this.tt('versionCopied').replace('{v}', v),
|
|
53960
|
-
color: 'dark',
|
|
53961
|
-
duration: 1500,
|
|
53962
|
-
});
|
|
53963
|
-
}
|
|
53964
|
-
catch {
|
|
53965
|
-
// clipboard no disponible — silencioso
|
|
53966
|
-
}
|
|
53967
|
-
}
|
|
53968
|
-
/** Abre una URL externa en nueva tab. `mailto:` queda en la misma tab. */
|
|
53969
|
-
openUrl(url) {
|
|
53970
|
-
if (url.startsWith('mailto:')) {
|
|
53971
|
-
window.location.href = url;
|
|
53972
|
-
return;
|
|
53973
|
-
}
|
|
53974
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
53975
|
-
}
|
|
53976
|
-
/**
|
|
53977
|
-
* "Calificar la app" — abre `val-content-reaction` DIRECTAMENTE como sheet
|
|
53978
|
-
* modal (sin wrapper local — decisión CTO). El componente persiste la reacción
|
|
53979
|
-
* contra el backend de feedback y muestra su propio estado de "gracias"; el
|
|
53980
|
-
* sheet se cierra arrastrando (breakpoint 0). El namespace i18n
|
|
53981
|
-
* `ContentReaction` lo auto-registra `ContentReactionComponent` en su
|
|
53982
|
-
* constructor, que corre al crear el modal — sin riesgo de key faltante.
|
|
53983
|
-
*
|
|
53984
|
-
* Sheet (bottom sheet): el contenido scrollea dentro y el botón "Enviar" queda
|
|
53985
|
-
* siempre alcanzable, también en viewports chicos.
|
|
53986
|
-
*/
|
|
53987
|
-
async openRating() {
|
|
53988
|
-
const reactionProps = {
|
|
53989
|
-
// entityType "app" distingue esta reacción de las de artículos/FAQs.
|
|
53990
|
-
entityRef: { entityType: 'app', entityId: this.resolvedConfig().ratingEntityId },
|
|
53991
|
-
question: this.tt('rateQuestion'),
|
|
53992
|
-
commentPlaceholder: this.tt('rateComment'),
|
|
53993
|
-
thankYouMessage: this.tt('rateThanks'),
|
|
53994
|
-
showComment: true,
|
|
53995
|
-
showThankYou: true,
|
|
53996
|
-
// Modal one-shot: NO bloquear el render con el check de rating previo.
|
|
53997
|
-
skipInitialCheck: true,
|
|
53998
|
-
emojis: ['😕', '🙂', '😍'],
|
|
53999
|
-
emojiLabels: [this.tt('rateBad'), this.tt('rateMid'), this.tt('rateGood')],
|
|
54000
|
-
};
|
|
54001
|
-
await this.modalService.open({
|
|
54002
|
-
component: ContentReactionComponent,
|
|
54003
|
-
componentProps: { props: reactionProps },
|
|
54004
|
-
breakpoints: {
|
|
54005
|
-
initialBreakpoint: 0.9,
|
|
54006
|
-
breakpoints: [0, 0.9, 1],
|
|
54007
|
-
showHandle: true,
|
|
54008
|
-
handleBehavior: 'cycle',
|
|
54009
|
-
},
|
|
54010
|
-
});
|
|
54011
|
-
this.resolvedConfig().onRated?.();
|
|
54012
|
-
}
|
|
54013
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
54014
|
-
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: `
|
|
54015
|
-
<ion-grid class="about-grid">
|
|
54016
|
-
<!-- HERO: logo + name + version -->
|
|
54017
|
-
<div class="about-hero">
|
|
54018
|
-
<img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
|
|
54019
|
-
<val-title
|
|
54020
|
-
[props]="{
|
|
54021
|
-
content: brandName(),
|
|
54022
|
-
size: 'xlarge',
|
|
54023
|
-
color: 'dark',
|
|
54024
|
-
bold: true,
|
|
54025
|
-
}"
|
|
54026
|
-
/>
|
|
54027
|
-
<val-text
|
|
54028
|
-
[props]="{
|
|
54029
|
-
content: tt('tagline'),
|
|
54030
|
-
size: 'medium',
|
|
54031
|
-
color: 'dark',
|
|
54032
|
-
bold: false,
|
|
54033
|
-
}"
|
|
54034
|
-
/>
|
|
54035
|
-
@if (version()) {
|
|
54036
|
-
<div
|
|
54037
|
-
class="version-row"
|
|
54038
|
-
(click)="copyVersion()"
|
|
54039
|
-
role="button"
|
|
54040
|
-
[attr.aria-label]="tt('versionLabel') + ' ' + version()"
|
|
54041
|
-
>
|
|
54042
|
-
<val-pill
|
|
54043
|
-
[props]="{
|
|
54044
|
-
label: tt('versionLabel') + ' ' + version(),
|
|
54045
|
-
color: 'dark',
|
|
54046
|
-
weight: 'bold',
|
|
54047
|
-
size: 'small',
|
|
54048
|
-
}"
|
|
54049
|
-
/>
|
|
54050
|
-
<ion-icon name="copy-outline" class="version-copy-icon" />
|
|
54051
|
-
</div>
|
|
54052
|
-
}
|
|
54053
|
-
</div>
|
|
54054
|
-
|
|
54055
|
-
<!-- DESCRIPTION -->
|
|
54056
|
-
<section class="about-section">
|
|
54057
|
-
<val-title [props]="sectionTitle('descTitle')" />
|
|
54058
|
-
<val-text
|
|
54059
|
-
[props]="{
|
|
54060
|
-
content: tt('descBody'),
|
|
54061
|
-
size: 'medium',
|
|
54062
|
-
color: 'dark',
|
|
54063
|
-
bold: false,
|
|
54064
|
-
}"
|
|
54065
|
-
/>
|
|
54066
|
-
</section>
|
|
54067
|
-
|
|
54068
|
-
<!-- ACTIONS — rating built-in + acciones del consumer. -->
|
|
54069
|
-
@if (showRating() || actions().length > 0) {
|
|
54070
|
-
<section class="about-section about-section--reviews">
|
|
54071
|
-
<val-title [props]="sectionTitle('actionsTitle')" />
|
|
54072
|
-
<div class="about-actions">
|
|
54073
|
-
@if (showRating()) {
|
|
54074
|
-
<val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
|
|
54075
|
-
}
|
|
54076
|
-
@for (action of actions(); track action.label) {
|
|
54077
|
-
<val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
|
|
54078
|
-
}
|
|
54079
|
-
</div>
|
|
54080
|
-
</section>
|
|
54081
|
-
}
|
|
54082
|
-
|
|
54083
|
-
<!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
|
|
54084
|
-
@if (socialLinks().length > 0) {
|
|
54085
|
-
<section class="about-section">
|
|
54086
|
-
<val-title [props]="sectionTitle('socialTitle')" />
|
|
54087
|
-
<val-text
|
|
54088
|
-
[props]="{
|
|
54089
|
-
content: tt('socialDesc'),
|
|
54090
|
-
size: 'medium',
|
|
54091
|
-
color: 'dark',
|
|
54092
|
-
bold: false,
|
|
54093
|
-
}"
|
|
54094
|
-
/>
|
|
54095
|
-
<div class="about-social">
|
|
54096
|
-
@for (s of socialLinks(); track s.url) {
|
|
54097
|
-
<ion-button
|
|
54098
|
-
fill="clear"
|
|
54099
|
-
size="small"
|
|
54100
|
-
class="social-btn"
|
|
54101
|
-
[attr.aria-label]="s.label"
|
|
54102
|
-
(click)="openUrl(s.url)"
|
|
54103
|
-
>
|
|
54104
|
-
<ion-icon [name]="s.icon" slot="icon-only" />
|
|
54105
|
-
</ion-button>
|
|
54106
|
-
}
|
|
54107
|
-
</div>
|
|
54108
|
-
</section>
|
|
54109
|
-
}
|
|
54110
|
-
|
|
54111
|
-
<!-- FOOTER -->
|
|
54112
|
-
<footer class="about-footer">
|
|
54113
|
-
<val-text
|
|
54114
|
-
[props]="{
|
|
54115
|
-
content: copyright(),
|
|
54116
|
-
size: 'small',
|
|
54117
|
-
color: 'dark',
|
|
54118
|
-
bold: false,
|
|
54119
|
-
}"
|
|
54120
|
-
/>
|
|
54121
|
-
</footer>
|
|
54122
|
-
</ion-grid>
|
|
54123
|
-
`, 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"] }] }); }
|
|
54124
|
-
}
|
|
54125
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AboutViewComponent, decorators: [{
|
|
54126
|
-
type: Component,
|
|
54127
|
-
args: [{ selector: 'val-about-view', standalone: true, imports: [IonButton, IonGrid, IonIcon, PillComponent, TitleComponent, TextComponent, ActionCardComponent], template: `
|
|
54128
|
-
<ion-grid class="about-grid">
|
|
54129
|
-
<!-- HERO: logo + name + version -->
|
|
54130
|
-
<div class="about-hero">
|
|
54131
|
-
<img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
|
|
54132
|
-
<val-title
|
|
54133
|
-
[props]="{
|
|
54134
|
-
content: brandName(),
|
|
54135
|
-
size: 'xlarge',
|
|
54136
|
-
color: 'dark',
|
|
54137
|
-
bold: true,
|
|
54138
|
-
}"
|
|
54139
|
-
/>
|
|
54140
|
-
<val-text
|
|
54141
|
-
[props]="{
|
|
54142
|
-
content: tt('tagline'),
|
|
54143
|
-
size: 'medium',
|
|
54144
|
-
color: 'dark',
|
|
54145
|
-
bold: false,
|
|
54146
|
-
}"
|
|
54147
|
-
/>
|
|
54148
|
-
@if (version()) {
|
|
54149
|
-
<div
|
|
54150
|
-
class="version-row"
|
|
54151
|
-
(click)="copyVersion()"
|
|
54152
|
-
role="button"
|
|
54153
|
-
[attr.aria-label]="tt('versionLabel') + ' ' + version()"
|
|
54154
|
-
>
|
|
54155
|
-
<val-pill
|
|
54156
|
-
[props]="{
|
|
54157
|
-
label: tt('versionLabel') + ' ' + version(),
|
|
54158
|
-
color: 'dark',
|
|
54159
|
-
weight: 'bold',
|
|
54160
|
-
size: 'small',
|
|
54161
|
-
}"
|
|
54162
|
-
/>
|
|
54163
|
-
<ion-icon name="copy-outline" class="version-copy-icon" />
|
|
54164
|
-
</div>
|
|
54165
|
-
}
|
|
54166
|
-
</div>
|
|
54167
|
-
|
|
54168
|
-
<!-- DESCRIPTION -->
|
|
54169
|
-
<section class="about-section">
|
|
54170
|
-
<val-title [props]="sectionTitle('descTitle')" />
|
|
54171
|
-
<val-text
|
|
54172
|
-
[props]="{
|
|
54173
|
-
content: tt('descBody'),
|
|
54174
|
-
size: 'medium',
|
|
54175
|
-
color: 'dark',
|
|
54176
|
-
bold: false,
|
|
54177
|
-
}"
|
|
54178
|
-
/>
|
|
54179
|
-
</section>
|
|
54180
|
-
|
|
54181
|
-
<!-- ACTIONS — rating built-in + acciones del consumer. -->
|
|
54182
|
-
@if (showRating() || actions().length > 0) {
|
|
54183
|
-
<section class="about-section about-section--reviews">
|
|
54184
|
-
<val-title [props]="sectionTitle('actionsTitle')" />
|
|
54185
|
-
<div class="about-actions">
|
|
54186
|
-
@if (showRating()) {
|
|
54187
|
-
<val-action-card [props]="rateActionCard()" (onClick)="openRating()" />
|
|
54188
|
-
}
|
|
54189
|
-
@for (action of actions(); track action.label) {
|
|
54190
|
-
<val-action-card [props]="extraActionCard(action)" (onClick)="action.onClick()" />
|
|
54191
|
-
}
|
|
54192
|
-
</div>
|
|
54193
|
-
</section>
|
|
54194
|
-
}
|
|
54195
|
-
|
|
54196
|
-
<!-- SOCIAL — penúltimo, justo antes del footer de derechos. -->
|
|
54197
|
-
@if (socialLinks().length > 0) {
|
|
54198
|
-
<section class="about-section">
|
|
54199
|
-
<val-title [props]="sectionTitle('socialTitle')" />
|
|
54200
|
-
<val-text
|
|
54201
|
-
[props]="{
|
|
54202
|
-
content: tt('socialDesc'),
|
|
54203
|
-
size: 'medium',
|
|
54204
|
-
color: 'dark',
|
|
54205
|
-
bold: false,
|
|
54206
|
-
}"
|
|
54207
|
-
/>
|
|
54208
|
-
<div class="about-social">
|
|
54209
|
-
@for (s of socialLinks(); track s.url) {
|
|
54210
|
-
<ion-button
|
|
54211
|
-
fill="clear"
|
|
54212
|
-
size="small"
|
|
54213
|
-
class="social-btn"
|
|
54214
|
-
[attr.aria-label]="s.label"
|
|
54215
|
-
(click)="openUrl(s.url)"
|
|
54216
|
-
>
|
|
54217
|
-
<ion-icon [name]="s.icon" slot="icon-only" />
|
|
54218
|
-
</ion-button>
|
|
54219
|
-
}
|
|
54220
|
-
</div>
|
|
54221
|
-
</section>
|
|
54222
|
-
}
|
|
54223
|
-
|
|
54224
|
-
<!-- FOOTER -->
|
|
54225
|
-
<footer class="about-footer">
|
|
54226
|
-
<val-text
|
|
54227
|
-
[props]="{
|
|
54228
|
-
content: copyright(),
|
|
54229
|
-
size: 'small',
|
|
54230
|
-
color: 'dark',
|
|
54231
|
-
bold: false,
|
|
54232
|
-
}"
|
|
54233
|
-
/>
|
|
54234
|
-
</footer>
|
|
54235
|
-
</ion-grid>
|
|
54236
|
-
`, 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"] }]
|
|
54237
|
-
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
54238
|
-
type: Input
|
|
54239
|
-
}] } });
|
|
54240
|
-
|
|
54241
|
-
/**
|
|
54242
|
-
* Configuración acotada de `val-about-view` (object-first). La vista About es
|
|
54243
|
-
* app-specific por naturaleza (logo, nombre, versión, redes, acciones extra),
|
|
54244
|
-
* así que lo propio de cada app se parametriza como **DATA de config** — NO como
|
|
54245
|
-
* slots arbitrarios (ADR-021: una vista full-feature no abre slots; si falta
|
|
54246
|
-
* algo, se promueve un nuevo punto de extensión).
|
|
54247
|
-
*
|
|
54248
|
-
* Ejes de variación permitidos:
|
|
54249
|
-
* - **branding** — `brand.name` / `brand.logoSrc` (qué muestra el HERO).
|
|
54250
|
-
* - **versión** — `version` la pasa el consumer (la lib NO puede importar el
|
|
54251
|
-
* `version.ts` de la app). Si no se pasa, se oculta el pill de versión.
|
|
54252
|
-
* - **redes** — `socialLinks`; si está vacío se oculta la sección social.
|
|
54253
|
-
* - **rating** — `showRating` + `ratingEntityId` + `onRated`.
|
|
54254
|
-
* - **acciones extra** — `actions[]` genéricas que el consumer agrega junto a
|
|
54255
|
-
* la acción de rating built-in.
|
|
54256
|
-
* - **branding i18n** — `i18nNamespace` (default `'Settings.About'`).
|
|
54257
|
-
*/
|
|
54258
|
-
|
|
54259
|
-
/**
|
|
54260
|
-
* Helper para montar la vista About (`val-about-view`) como ruta en una app del
|
|
54261
|
-
* factory. El `config` se pasa por route `data` (`aboutConfig`) y el componente
|
|
54262
|
-
* lo lee como fallback de su `@Input() config` (el `@Input` gana).
|
|
54263
|
-
*
|
|
54264
|
-
* @example
|
|
54265
|
-
* ```ts
|
|
54266
|
-
* // settings.routes.ts
|
|
54267
|
-
* import { APP_VERSION } from '../version';
|
|
54268
|
-
*
|
|
54269
|
-
* export const settingsRoutes: Routes = [
|
|
54270
|
-
* ...provideValtechAboutRoutes({ config: { version: APP_VERSION } }), // → /app/about
|
|
54271
|
-
* { path: 'preferences', loadComponent: () => ... },
|
|
54272
|
-
* ];
|
|
54273
|
-
*
|
|
54274
|
-
* // con redes y acciones extra:
|
|
54275
|
-
* ...provideValtechAboutRoutes({
|
|
54276
|
-
* config: {
|
|
54277
|
-
* version: APP_VERSION,
|
|
54278
|
-
* socialLinks: VALTECH_SOCIAL_LINKS.map(s => ({ icon: s.icon, url: s.url, label: s.name })),
|
|
54279
|
-
* actions: [{ label: 'Compartir', icon: 'share-social-outline', onClick: () => share() }],
|
|
54280
|
-
* },
|
|
54281
|
-
* }),
|
|
54282
|
-
* ```
|
|
54283
|
-
*/
|
|
54284
|
-
function provideValtechAboutRoutes(opts) {
|
|
54285
|
-
return [
|
|
54286
|
-
{
|
|
54287
|
-
path: opts?.path ?? 'about',
|
|
54288
|
-
component: AboutViewComponent,
|
|
54289
|
-
data: { aboutConfig: opts?.config },
|
|
54290
|
-
},
|
|
54291
|
-
];
|
|
54292
|
-
}
|
|
54293
|
-
|
|
54294
52500
|
/** Built-in label sets for the three platform locales. */
|
|
54295
52501
|
const CALLOUT_LABELS = {
|
|
54296
52502
|
es: {
|
|
@@ -55847,11 +54053,15 @@ class AuthBackgroundComponent {
|
|
|
55847
54053
|
const isThick = i % 5 === 0;
|
|
55848
54054
|
const isMedium = i % 3 === 0 && !isThick;
|
|
55849
54055
|
const amplitude = 10 + Math.random() * 20;
|
|
55850
|
-
|
|
54056
|
+
// Frecuencia ENTERA (ciclos completos sobre el ancho 1440). Imprescindible
|
|
54057
|
+
// para el loop seamless: con freq entero, path(x) === path(x ± 1440), así
|
|
54058
|
+
// el translateX de una vuelta (1440px) calza exacto y NO hay salto/corte.
|
|
54059
|
+
const frequency = 1 + Math.floor(Math.random() * 3); // 1 | 2 | 3
|
|
55851
54060
|
const phase = Math.random() * Math.PI * 2;
|
|
55852
|
-
// Path extendido
|
|
55853
|
-
|
|
55854
|
-
|
|
54061
|
+
// Path extendido a -1480 .. 2920 (≥ una vuelta de bleed a cada lado) para
|
|
54062
|
+
// que el desplazamiento de ±1440px nunca deje el viewBox sin onda.
|
|
54063
|
+
let path = `M-1480,${y}`;
|
|
54064
|
+
for (let x = -1480; x <= 2920; x += 40) {
|
|
55855
54065
|
const waveY = y + amplitude * Math.sin((x / 1440) * Math.PI * frequency * 2 + phase);
|
|
55856
54066
|
path += ` L${x},${waveY.toFixed(1)}`;
|
|
55857
54067
|
}
|
|
@@ -55862,12 +54072,12 @@ class AuthBackgroundComponent {
|
|
|
55862
54072
|
color,
|
|
55863
54073
|
thickness: isThick ? 1.2 : isMedium ? 0.7 : 0.3,
|
|
55864
54074
|
opacity: isThick ? 0.5 : isMedium ? 0.4 : 0.3,
|
|
55865
|
-
//
|
|
55866
|
-
|
|
55867
|
-
|
|
55868
|
-
//
|
|
55869
|
-
travelDuration:
|
|
55870
|
-
travelDelay: -Math.random() *
|
|
54075
|
+
// Drift vertical sutil (no "equalizer"): da vida sin saltos.
|
|
54076
|
+
bob: 4 + Math.random() * 10,
|
|
54077
|
+
// Flujo horizontal continuo. Duraciones variadas = parallax/profundidad.
|
|
54078
|
+
// delay negativo desincroniza el arranque de cada capa.
|
|
54079
|
+
travelDuration: 14 + Math.random() * 22,
|
|
54080
|
+
travelDelay: -Math.random() * 30,
|
|
55871
54081
|
travelDirection: i % 2 === 0 ? 'right' : 'left',
|
|
55872
54082
|
};
|
|
55873
54083
|
});
|
|
@@ -55892,8 +54102,7 @@ class AuthBackgroundComponent {
|
|
|
55892
54102
|
[class.travel-left]="wave.travelDirection === 'left'"
|
|
55893
54103
|
[class.travel-right]="wave.travelDirection === 'right'"
|
|
55894
54104
|
[style.opacity]="wave.opacity"
|
|
55895
|
-
[style.--
|
|
55896
|
-
[style.--pulse-delay]="wave.pulseDelay + 's'"
|
|
54105
|
+
[style.--bob]="wave.bob + 'px'"
|
|
55897
54106
|
[style.--travel-duration]="wave.travelDuration + 's'"
|
|
55898
54107
|
[style.--travel-delay]="wave.travelDelay + 's'"
|
|
55899
54108
|
/>
|
|
@@ -55905,7 +54114,7 @@ class AuthBackgroundComponent {
|
|
|
55905
54114
|
<ng-content></ng-content>
|
|
55906
54115
|
</div>
|
|
55907
54116
|
</section>
|
|
55908
|
-
`, isInline: true, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;
|
|
54117
|
+
`, isInline: true, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;will-change:transform;animation:wave-flow-right var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-flow-left var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}@keyframes wave-flow-right{0%{transform:translate(0)}25%{transform:translate(-360px,calc(var(--bob, 0px) * -1))}50%{transform:translate(-720px)}75%{transform:translate(-1080px,var(--bob, 0px))}to{transform:translate(-1440px)}}@keyframes wave-flow-left{0%{transform:translate(0)}25%{transform:translate(360px,var(--bob, 0px))}50%{transform:translate(720px)}75%{transform:translate(1080px,calc(var(--bob, 0px) * -1))}to{transform:translate(1440px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
|
|
55909
54118
|
}
|
|
55910
54119
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthBackgroundComponent, decorators: [{
|
|
55911
54120
|
type: Component,
|
|
@@ -55927,8 +54136,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
55927
54136
|
[class.travel-left]="wave.travelDirection === 'left'"
|
|
55928
54137
|
[class.travel-right]="wave.travelDirection === 'right'"
|
|
55929
54138
|
[style.opacity]="wave.opacity"
|
|
55930
|
-
[style.--
|
|
55931
|
-
[style.--pulse-delay]="wave.pulseDelay + 's'"
|
|
54139
|
+
[style.--bob]="wave.bob + 'px'"
|
|
55932
54140
|
[style.--travel-duration]="wave.travelDuration + 's'"
|
|
55933
54141
|
[style.--travel-delay]="wave.travelDelay + 's'"
|
|
55934
54142
|
/>
|
|
@@ -55940,7 +54148,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
55940
54148
|
<ng-content></ng-content>
|
|
55941
54149
|
</div>
|
|
55942
54150
|
</section>
|
|
55943
|
-
`, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;
|
|
54151
|
+
`, styles: [":host{display:block;--auth-bg-start: #ffffff;--auth-bg-mid: #f8f9fa;--auth-bg-end: #f0f0f0;--auth-glass-bg: rgba(255, 255, 255, .3);--auth-grain-opacity: .035;--auth-wave-color: var(--ion-color-primary, #6366f1)}:host-context(body.dark),:host-context(.dark),:host-context(.ion-palette-dark),:host-context(html.ion-palette-dark),:host-context([data-theme=\"dark\"]){--auth-bg-start: #0f0f1a;--auth-bg-mid: #1a1a2e;--auth-bg-end: #16213e;--auth-glass-bg: rgba(0, 0, 0, .3);--auth-grain-opacity: .05;--auth-wave-color: var(--ion-color-primary, #818cf8)}.auth-background{position:relative;display:flex;align-items:center;padding:0;background:linear-gradient(135deg,var(--auth-bg-start) 0%,var(--auth-bg-mid) 50%,var(--auth-bg-end) 100%);overflow:hidden}.grain{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:var(--auth-grain-opacity);background-image:url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")}.glass-layer{position:absolute;inset:0;pointer-events:none;z-index:1;background:var(--auth-glass-bg);backdrop-filter:blur(3px) saturate(120%);-webkit-backdrop-filter:blur(3px) saturate(120%)}.wave-bg{position:absolute;inset:0;pointer-events:none;z-index:0;transition:filter .3s ease}:host-context(body.dark) .wave-bg,:host-context(.dark) .wave-bg,:host-context(.ion-palette-dark) .wave-bg,:host-context(html.ion-palette-dark) .wave-bg,:host-context([data-theme=\"dark\"]) .wave-bg{filter:invert(1) opacity(.6)}.waves{width:100%;height:100%;overflow:visible}.waves path{fill:none;will-change:transform;animation:wave-flow-right var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}.waves path.travel-left{animation:wave-flow-left var(--travel-duration, 18s) linear var(--travel-delay, 0s) infinite}@keyframes wave-flow-right{0%{transform:translate(0)}25%{transform:translate(-360px,calc(var(--bob, 0px) * -1))}50%{transform:translate(-720px)}75%{transform:translate(-1080px,var(--bob, 0px))}to{transform:translate(-1440px)}}@keyframes wave-flow-left{0%{transform:translate(0)}25%{transform:translate(360px,var(--bob, 0px))}50%{transform:translate(720px)}75%{transform:translate(1080px,calc(var(--bob, 0px) * -1))}to{transform:translate(1440px)}}@media (prefers-reduced-motion: reduce){.waves path{animation:none}}.content-layer{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:center}\n"] }]
|
|
55944
54152
|
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
55945
54153
|
type: Input
|
|
55946
54154
|
}] } });
|
|
@@ -60388,6 +58596,263 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
60388
58596
|
type: Output
|
|
60389
58597
|
}] } });
|
|
60390
58598
|
|
|
58599
|
+
/**
|
|
58600
|
+
* Defaults i18n del componente (es/en). El componente los auto-registra en su
|
|
58601
|
+
* constructor si el consumer no proveyó el namespace `ContentReaction`. Esto
|
|
58602
|
+
* garantiza que NUNCA haya una key faltante: una key faltante se evalúa en el
|
|
58603
|
+
* template por cada change-detection → `console.warn` por CD → con el
|
|
58604
|
+
* `val-debug-console` activo (espeja console.* a un signal) se dispara un loop
|
|
58605
|
+
* infinito que congela el navegador en dev. Auto-registrar lo elimina de raíz.
|
|
58606
|
+
*/
|
|
58607
|
+
const CONTENT_REACTION_I18N = {
|
|
58608
|
+
es: {
|
|
58609
|
+
question: '¿Te resultó útil este contenido?',
|
|
58610
|
+
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
58611
|
+
submit: 'Enviar opinión',
|
|
58612
|
+
update: 'Actualizar opinión',
|
|
58613
|
+
thankYou: '¡Gracias por tu opinión!',
|
|
58614
|
+
submitted: 'Tu opinión ha sido registrada',
|
|
58615
|
+
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
58616
|
+
positiveLabel: 'Sí, mucho',
|
|
58617
|
+
negativeLabel: 'No tanto',
|
|
58618
|
+
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
58619
|
+
},
|
|
58620
|
+
en: {
|
|
58621
|
+
question: 'Was this content helpful?',
|
|
58622
|
+
commentPlaceholder: 'Tell us more (optional)...',
|
|
58623
|
+
submit: 'Send feedback',
|
|
58624
|
+
update: 'Update feedback',
|
|
58625
|
+
thankYou: 'Thanks for your feedback!',
|
|
58626
|
+
submitted: 'Your feedback has been recorded',
|
|
58627
|
+
errorSubmitting: 'Failed to send. Please try again.',
|
|
58628
|
+
positiveLabel: 'Yes, a lot',
|
|
58629
|
+
negativeLabel: 'Not really',
|
|
58630
|
+
negativeFeedbackTitle: "Tell us what you didn't like?",
|
|
58631
|
+
},
|
|
58632
|
+
};
|
|
58633
|
+
/**
|
|
58634
|
+
* Componente para reacciones de contenido con emojis.
|
|
58635
|
+
*
|
|
58636
|
+
* @example
|
|
58637
|
+
* ```html
|
|
58638
|
+
* <val-content-reaction
|
|
58639
|
+
* [props]="{
|
|
58640
|
+
* entityRef: { entityType: 'article', entityId: 'art-123' },
|
|
58641
|
+
* question: '¿Te fue útil este artículo?'
|
|
58642
|
+
* }"
|
|
58643
|
+
* (reactionSubmit)="onReactionSubmit($event)"
|
|
58644
|
+
* />
|
|
58645
|
+
* ```
|
|
58646
|
+
*/
|
|
58647
|
+
class ContentReactionComponent {
|
|
58648
|
+
constructor() {
|
|
58649
|
+
this.feedbackService = inject(FeedbackService);
|
|
58650
|
+
this.toast = inject(ToastService);
|
|
58651
|
+
this.i18n = inject(I18nService);
|
|
58652
|
+
this.auth = inject(AuthService, { optional: true });
|
|
58653
|
+
this.isConfigured = computed(() => this.feedbackService.isConfigured());
|
|
58654
|
+
this.props = {};
|
|
58655
|
+
this.reactionSubmit = new EventEmitter();
|
|
58656
|
+
this.reactionChange = new EventEmitter();
|
|
58657
|
+
// Estado reactivo
|
|
58658
|
+
this.state = signal({
|
|
58659
|
+
selectedValue: null,
|
|
58660
|
+
comment: '',
|
|
58661
|
+
isLoading: false,
|
|
58662
|
+
isSubmitted: false,
|
|
58663
|
+
hadPreviousReaction: false,
|
|
58664
|
+
error: null,
|
|
58665
|
+
});
|
|
58666
|
+
// Valores por defecto
|
|
58667
|
+
this.defaultEmojis = ['😞', '😐', '😊'];
|
|
58668
|
+
this.defaultLabels = ['No me ayudó', 'Regular', 'Muy útil'];
|
|
58669
|
+
// Computed properties
|
|
58670
|
+
this.resolvedProps = computed(() => ({
|
|
58671
|
+
entityRef: this.props.entityRef,
|
|
58672
|
+
question: this.props.question || this.t('question'),
|
|
58673
|
+
showComment: this.props.showComment ?? true,
|
|
58674
|
+
commentOnValues: this.props.commentOnValues,
|
|
58675
|
+
commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
|
|
58676
|
+
maxCommentLength: this.props.maxCommentLength ?? 500,
|
|
58677
|
+
emojis: this.props.emojis || this.defaultEmojis,
|
|
58678
|
+
emojiLabels: this.props.emojiLabels || this.defaultLabels,
|
|
58679
|
+
showThankYou: this.props.showThankYou ?? true,
|
|
58680
|
+
thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
|
|
58681
|
+
disabled: this.props.disabled ?? false,
|
|
58682
|
+
readonly: this.props.readonly ?? false,
|
|
58683
|
+
// Variante: 'emoji' cuando se declara explícitamente o cuando se pasan emojis custom.
|
|
58684
|
+
variant: this.props.variant ?? (this.props.emojis ? 'emoji' : 'buttons'),
|
|
58685
|
+
}));
|
|
58686
|
+
this.activeReactionValues = computed(() => this.resolvedProps().emojis.length === 2 ? ['negative', 'positive'] : ['negative', 'neutral', 'positive']);
|
|
58687
|
+
this.showCommentField = computed(() => {
|
|
58688
|
+
const sel = this.state().selectedValue;
|
|
58689
|
+
if (!sel)
|
|
58690
|
+
return false;
|
|
58691
|
+
const commentOn = this.resolvedProps().commentOnValues;
|
|
58692
|
+
if (commentOn !== undefined)
|
|
58693
|
+
return commentOn.includes(sel);
|
|
58694
|
+
return this.resolvedProps().showComment;
|
|
58695
|
+
});
|
|
58696
|
+
this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
|
|
58697
|
+
// Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
|
|
58698
|
+
// namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
|
|
58699
|
+
// CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
|
|
58700
|
+
if (!this.i18n.hasNamespace('ContentReaction')) {
|
|
58701
|
+
this.i18n.registerContent('ContentReaction', CONTENT_REACTION_I18N);
|
|
58702
|
+
}
|
|
58703
|
+
}
|
|
58704
|
+
ngOnInit() {
|
|
58705
|
+
this.loadPreviousReaction();
|
|
58706
|
+
}
|
|
58707
|
+
ngOnChanges(changes) {
|
|
58708
|
+
if (changes['props'] && !changes['props'].firstChange) {
|
|
58709
|
+
this.loadPreviousReaction();
|
|
58710
|
+
}
|
|
58711
|
+
}
|
|
58712
|
+
async loadPreviousReaction() {
|
|
58713
|
+
if (!this.props.entityRef)
|
|
58714
|
+
return;
|
|
58715
|
+
// Si hay initialValue, usarlo directamente sin consultar
|
|
58716
|
+
if (this.props.initialValue) {
|
|
58717
|
+
this.state.update(s => ({
|
|
58718
|
+
...s,
|
|
58719
|
+
selectedValue: this.props.initialValue,
|
|
58720
|
+
hadPreviousReaction: true,
|
|
58721
|
+
isLoading: false,
|
|
58722
|
+
isSubmitted: true,
|
|
58723
|
+
}));
|
|
58724
|
+
return;
|
|
58725
|
+
}
|
|
58726
|
+
// Si skipInitialCheck está activo, no consultar
|
|
58727
|
+
if (this.props.skipInitialCheck) {
|
|
58728
|
+
return;
|
|
58729
|
+
}
|
|
58730
|
+
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
58731
|
+
try {
|
|
58732
|
+
const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
|
|
58733
|
+
if (check.hasFeedback && check.reactionValue) {
|
|
58734
|
+
this.state.update(s => ({
|
|
58735
|
+
...s,
|
|
58736
|
+
selectedValue: check.reactionValue,
|
|
58737
|
+
hadPreviousReaction: true,
|
|
58738
|
+
isLoading: false,
|
|
58739
|
+
isSubmitted: true,
|
|
58740
|
+
}));
|
|
58741
|
+
}
|
|
58742
|
+
else {
|
|
58743
|
+
this.state.update(s => ({ ...s, isLoading: false }));
|
|
58744
|
+
}
|
|
58745
|
+
}
|
|
58746
|
+
catch (error) {
|
|
58747
|
+
console.error('Error loading previous reaction:', error);
|
|
58748
|
+
this.state.update(s => ({ ...s, isLoading: false }));
|
|
58749
|
+
}
|
|
58750
|
+
}
|
|
58751
|
+
selectReaction(value) {
|
|
58752
|
+
if (this.resolvedProps().disabled || this.resolvedProps().readonly)
|
|
58753
|
+
return;
|
|
58754
|
+
const previousValue = this.state().selectedValue;
|
|
58755
|
+
this.state.update(s => ({
|
|
58756
|
+
...s,
|
|
58757
|
+
selectedValue: value,
|
|
58758
|
+
isSubmitted: false,
|
|
58759
|
+
error: null,
|
|
58760
|
+
}));
|
|
58761
|
+
this.reactionChange.emit({ value, previousValue });
|
|
58762
|
+
}
|
|
58763
|
+
async submitReaction() {
|
|
58764
|
+
const currentState = this.state();
|
|
58765
|
+
const props = this.resolvedProps();
|
|
58766
|
+
if (!currentState.selectedValue || props.disabled)
|
|
58767
|
+
return;
|
|
58768
|
+
this.state.update(s => ({ ...s, isLoading: true, error: null }));
|
|
58769
|
+
try {
|
|
58770
|
+
// Determinar si usar endpoint anónimo o autenticado
|
|
58771
|
+
const isAuthenticated = this.auth?.isAuthenticated() ?? false;
|
|
58772
|
+
const useAnonymous = this.props.allowAnonymous && !isAuthenticated;
|
|
58773
|
+
if (useAnonymous) {
|
|
58774
|
+
// Usar endpoint anónimo (público)
|
|
58775
|
+
await this.feedbackService.createAnonymousReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
58776
|
+
}
|
|
58777
|
+
else {
|
|
58778
|
+
// Usar endpoint autenticado
|
|
58779
|
+
await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
58780
|
+
}
|
|
58781
|
+
this.state.update(s => ({
|
|
58782
|
+
...s,
|
|
58783
|
+
isLoading: false,
|
|
58784
|
+
isSubmitted: true,
|
|
58785
|
+
comment: '',
|
|
58786
|
+
hadPreviousReaction: !useAnonymous, // Solo para autenticados
|
|
58787
|
+
}));
|
|
58788
|
+
this.reactionSubmit.emit({
|
|
58789
|
+
value: currentState.selectedValue,
|
|
58790
|
+
comment: currentState.comment || undefined,
|
|
58791
|
+
entityRef: props.entityRef,
|
|
58792
|
+
isUpdate: currentState.hadPreviousReaction,
|
|
58793
|
+
});
|
|
58794
|
+
if (props.showThankYou) {
|
|
58795
|
+
this.toast.show({
|
|
58796
|
+
message: props.thankYouMessage,
|
|
58797
|
+
duration: 2000,
|
|
58798
|
+
});
|
|
58799
|
+
}
|
|
58800
|
+
}
|
|
58801
|
+
catch (error) {
|
|
58802
|
+
console.error('Error submitting reaction:', error);
|
|
58803
|
+
this.state.update(s => ({
|
|
58804
|
+
...s,
|
|
58805
|
+
isLoading: false,
|
|
58806
|
+
error: this.t('errorSubmitting'),
|
|
58807
|
+
}));
|
|
58808
|
+
this.toast.show({
|
|
58809
|
+
message: this.t('errorSubmitting'),
|
|
58810
|
+
duration: 3000,
|
|
58811
|
+
});
|
|
58812
|
+
}
|
|
58813
|
+
}
|
|
58814
|
+
updateComment(event) {
|
|
58815
|
+
const value = event.detail.value || '';
|
|
58816
|
+
this.state.update(s => ({ ...s, comment: value }));
|
|
58817
|
+
}
|
|
58818
|
+
getEmoji(index) {
|
|
58819
|
+
return this.resolvedProps().emojis[index];
|
|
58820
|
+
}
|
|
58821
|
+
getEmojiLabel(index) {
|
|
58822
|
+
return this.resolvedProps().emojiLabels[index];
|
|
58823
|
+
}
|
|
58824
|
+
isSelected(value) {
|
|
58825
|
+
return this.state().selectedValue === value;
|
|
58826
|
+
}
|
|
58827
|
+
t(key) {
|
|
58828
|
+
const translations = {
|
|
58829
|
+
question: '¿Te resultó útil este contenido?',
|
|
58830
|
+
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
58831
|
+
submit: 'Enviar opinión',
|
|
58832
|
+
update: 'Actualizar opinión',
|
|
58833
|
+
thankYou: '¡Gracias por tu opinión!',
|
|
58834
|
+
submitted: 'Tu opinión ha sido registrada',
|
|
58835
|
+
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
58836
|
+
positiveLabel: 'Sí, mucho',
|
|
58837
|
+
negativeLabel: 'No tanto',
|
|
58838
|
+
negativeFeedbackTitle: '¿Cuéntanos qué no te gustó?',
|
|
58839
|
+
};
|
|
58840
|
+
return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
|
|
58841
|
+
}
|
|
58842
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
58843
|
+
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"] }] }); }
|
|
58844
|
+
}
|
|
58845
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
|
|
58846
|
+
type: Component,
|
|
58847
|
+
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"] }]
|
|
58848
|
+
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
58849
|
+
type: Input
|
|
58850
|
+
}], reactionSubmit: [{
|
|
58851
|
+
type: Output
|
|
58852
|
+
}], reactionChange: [{
|
|
58853
|
+
type: Output
|
|
58854
|
+
}] } });
|
|
58855
|
+
|
|
60391
58856
|
const DEFAULT_SPLASH_SCREEN_CONFIG = {
|
|
60392
58857
|
fadeOutDuration: 300,
|
|
60393
58858
|
};
|
|
@@ -64240,5 +62705,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
64240
62705
|
* Generated bundle index. Do not edit.
|
|
64241
62706
|
*/
|
|
64242
62707
|
|
|
64243
|
-
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, ApiKeyService, ApiKeysModalComponent, 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, MemberImportModalComponent, 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 };
|
|
62708
|
+
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 };
|
|
64244
62709
|
//# sourceMappingURL=valtech-components.mjs.map
|