valtech-components 4.0.988 → 4.0.990
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/organisms/attachment-uploader/attachment-uploader.component.mjs +41 -2
- package/esm2022/lib/components/organisms/attachment-uploader/types.mjs +1 -1
- package/esm2022/lib/components/organisms/request-review-panel/request-review-panel.component.mjs +316 -481
- package/esm2022/lib/components/organisms/request-review-panel/request-review-panel.i18n.mjs +21 -0
- package/esm2022/lib/components/organisms/request-review-panel/types.mjs +2 -0
- package/esm2022/lib/components/organisms/survey-builder/survey-builder.component.mjs +24 -5
- package/esm2022/lib/components/organisms/survey-builder/types.mjs +1 -1
- package/esm2022/lib/services/payments/config.mjs +23 -0
- package/esm2022/lib/services/payments/index.mjs +4 -0
- package/esm2022/lib/services/payments/payments-gateway.service.mjs +69 -0
- package/esm2022/lib/services/payments/types.mjs +8 -0
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +4 -2
- package/fesm2022/valtech-components.mjs +793 -784
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/attachment-uploader/attachment-uploader.component.d.ts +14 -0
- package/lib/components/organisms/attachment-uploader/types.d.ts +30 -0
- package/lib/components/organisms/request-review-panel/request-review-panel.component.d.ts +72 -76
- package/lib/components/organisms/request-review-panel/request-review-panel.i18n.d.ts +2 -0
- package/lib/components/organisms/request-review-panel/types.d.ts +73 -0
- package/lib/components/organisms/survey-builder/survey-builder.component.d.ts +9 -2
- package/lib/components/organisms/survey-builder/types.d.ts +28 -1
- package/lib/services/payments/config.d.ts +14 -0
- package/lib/services/payments/index.d.ts +3 -0
- package/lib/services/payments/payments-gateway.service.d.ts +53 -0
- package/lib/services/payments/types.d.ts +33 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +3 -1
|
@@ -70,7 +70,7 @@ import fixWebmDuration from 'fix-webm-duration';
|
|
|
70
70
|
* Current version of valtech-components.
|
|
71
71
|
* This is automatically updated during the publish process.
|
|
72
72
|
*/
|
|
73
|
-
const VERSION = '4.0.
|
|
73
|
+
const VERSION = '4.0.990';
|
|
74
74
|
|
|
75
75
|
function evaluateValtechAccess(rule, context, features = {}, visitedFeatures = new Set()) {
|
|
76
76
|
if (rule == null)
|
|
@@ -37047,6 +37047,260 @@ const DEFAULT_FEEDBACK_TYPE_OPTIONS = [
|
|
|
37047
37047
|
*/
|
|
37048
37048
|
// Configuration
|
|
37049
37049
|
|
|
37050
|
+
/**
|
|
37051
|
+
* Default values for image processing
|
|
37052
|
+
*/
|
|
37053
|
+
const IMAGE_DEFAULTS = {
|
|
37054
|
+
maxWidth: 800,
|
|
37055
|
+
maxHeight: 800,
|
|
37056
|
+
quality: 0.8,
|
|
37057
|
+
mimeType: 'image/jpeg',
|
|
37058
|
+
maxSize: 10 * 1024 * 1024, // 10MB
|
|
37059
|
+
allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
|
|
37060
|
+
thumbnailSize: 150,
|
|
37061
|
+
};
|
|
37062
|
+
|
|
37063
|
+
/**
|
|
37064
|
+
* ImageService
|
|
37065
|
+
*
|
|
37066
|
+
* Service for image processing including compression, thumbnails, cropping and validation.
|
|
37067
|
+
* Uses HTML Canvas for all operations - no external dependencies.
|
|
37068
|
+
*
|
|
37069
|
+
* @example
|
|
37070
|
+
* ```typescript
|
|
37071
|
+
* const imageService = inject(ImageService);
|
|
37072
|
+
*
|
|
37073
|
+
* // Compress an image
|
|
37074
|
+
* const compressed = await imageService.compress(file, { maxWidth: 800, quality: 0.8 });
|
|
37075
|
+
*
|
|
37076
|
+
* // Generate thumbnail
|
|
37077
|
+
* const thumb = await imageService.thumbnail(file, 150);
|
|
37078
|
+
*
|
|
37079
|
+
* // Validate before processing
|
|
37080
|
+
* const validation = imageService.validate(file, { maxSize: 5 * 1024 * 1024 });
|
|
37081
|
+
* if (!validation.valid) {
|
|
37082
|
+
* console.error(validation.message);
|
|
37083
|
+
* }
|
|
37084
|
+
* ```
|
|
37085
|
+
*/
|
|
37086
|
+
class ImageService {
|
|
37087
|
+
/**
|
|
37088
|
+
* Compress an image maintaining aspect ratio
|
|
37089
|
+
* @param file - File or Blob to compress
|
|
37090
|
+
* @param options - Compression options
|
|
37091
|
+
* @returns Promise with processed image data
|
|
37092
|
+
*/
|
|
37093
|
+
async compress(file, options) {
|
|
37094
|
+
const opts = {
|
|
37095
|
+
maxWidth: options?.maxWidth ?? IMAGE_DEFAULTS.maxWidth,
|
|
37096
|
+
maxHeight: options?.maxHeight ?? IMAGE_DEFAULTS.maxHeight,
|
|
37097
|
+
quality: options?.quality ?? IMAGE_DEFAULTS.quality,
|
|
37098
|
+
mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
|
|
37099
|
+
};
|
|
37100
|
+
const img = await this.loadImage(file);
|
|
37101
|
+
const { width, height } = this.calculateDimensions(img.width, img.height, opts.maxWidth, opts.maxHeight);
|
|
37102
|
+
const canvas = document.createElement('canvas');
|
|
37103
|
+
canvas.width = width;
|
|
37104
|
+
canvas.height = height;
|
|
37105
|
+
const ctx = canvas.getContext('2d');
|
|
37106
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
37107
|
+
const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
|
|
37108
|
+
const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
|
|
37109
|
+
return {
|
|
37110
|
+
blob,
|
|
37111
|
+
dataUrl,
|
|
37112
|
+
width,
|
|
37113
|
+
height,
|
|
37114
|
+
size: blob.size,
|
|
37115
|
+
};
|
|
37116
|
+
}
|
|
37117
|
+
/**
|
|
37118
|
+
* Generate a square thumbnail from an image
|
|
37119
|
+
* @param file - File or Blob to process
|
|
37120
|
+
* @param size - Thumbnail size in pixels (default: 150)
|
|
37121
|
+
* @returns Promise with processed thumbnail
|
|
37122
|
+
*/
|
|
37123
|
+
async thumbnail(file, size) {
|
|
37124
|
+
const thumbSize = size ?? IMAGE_DEFAULTS.thumbnailSize;
|
|
37125
|
+
const img = await this.loadImage(file);
|
|
37126
|
+
// Calculate square crop from center
|
|
37127
|
+
const minDim = Math.min(img.width, img.height);
|
|
37128
|
+
const cropX = (img.width - minDim) / 2;
|
|
37129
|
+
const cropY = (img.height - minDim) / 2;
|
|
37130
|
+
const canvas = document.createElement('canvas');
|
|
37131
|
+
canvas.width = thumbSize;
|
|
37132
|
+
canvas.height = thumbSize;
|
|
37133
|
+
const ctx = canvas.getContext('2d');
|
|
37134
|
+
ctx.drawImage(img, cropX, cropY, minDim, minDim, 0, 0, thumbSize, thumbSize);
|
|
37135
|
+
const blob = await this.canvasToBlob(canvas, IMAGE_DEFAULTS.mimeType, 0.7 // Lower quality for thumbnails
|
|
37136
|
+
);
|
|
37137
|
+
const dataUrl = canvas.toDataURL(IMAGE_DEFAULTS.mimeType, 0.7);
|
|
37138
|
+
return {
|
|
37139
|
+
blob,
|
|
37140
|
+
dataUrl,
|
|
37141
|
+
width: thumbSize,
|
|
37142
|
+
height: thumbSize,
|
|
37143
|
+
size: blob.size,
|
|
37144
|
+
};
|
|
37145
|
+
}
|
|
37146
|
+
/**
|
|
37147
|
+
* Crop an image with specific coordinates
|
|
37148
|
+
* @param file - File or Blob to crop
|
|
37149
|
+
* @param cropData - Crop coordinates and dimensions
|
|
37150
|
+
* @param options - Optional compression options for output
|
|
37151
|
+
* @returns Promise with cropped image
|
|
37152
|
+
*/
|
|
37153
|
+
async crop(file, cropData, options) {
|
|
37154
|
+
const img = await this.loadImage(file);
|
|
37155
|
+
const opts = {
|
|
37156
|
+
quality: options?.quality ?? IMAGE_DEFAULTS.quality,
|
|
37157
|
+
mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
|
|
37158
|
+
};
|
|
37159
|
+
const canvas = document.createElement('canvas');
|
|
37160
|
+
canvas.width = cropData.width;
|
|
37161
|
+
canvas.height = cropData.height;
|
|
37162
|
+
const ctx = canvas.getContext('2d');
|
|
37163
|
+
ctx.drawImage(img, cropData.x, cropData.y, cropData.width, cropData.height, 0, 0, cropData.width, cropData.height);
|
|
37164
|
+
// Apply max dimensions if specified
|
|
37165
|
+
if (options?.maxWidth || options?.maxHeight) {
|
|
37166
|
+
return this.compress(await this.canvasToBlob(canvas, opts.mimeType, 1), options);
|
|
37167
|
+
}
|
|
37168
|
+
const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
|
|
37169
|
+
const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
|
|
37170
|
+
return {
|
|
37171
|
+
blob,
|
|
37172
|
+
dataUrl,
|
|
37173
|
+
width: cropData.width,
|
|
37174
|
+
height: cropData.height,
|
|
37175
|
+
size: blob.size,
|
|
37176
|
+
};
|
|
37177
|
+
}
|
|
37178
|
+
/**
|
|
37179
|
+
* Validate an image file before processing
|
|
37180
|
+
* @param file - File to validate
|
|
37181
|
+
* @param options - Validation options
|
|
37182
|
+
* @returns Validation result with error details if invalid
|
|
37183
|
+
*/
|
|
37184
|
+
validate(file, options) {
|
|
37185
|
+
const opts = {
|
|
37186
|
+
maxSize: options?.maxSize ?? IMAGE_DEFAULTS.maxSize,
|
|
37187
|
+
allowedTypes: options?.allowedTypes ?? IMAGE_DEFAULTS.allowedTypes,
|
|
37188
|
+
};
|
|
37189
|
+
// Check file type
|
|
37190
|
+
if (!opts.allowedTypes.includes(file.type)) {
|
|
37191
|
+
return {
|
|
37192
|
+
valid: false,
|
|
37193
|
+
error: 'invalidType',
|
|
37194
|
+
message: `Formato no válido. Usa: ${opts.allowedTypes.map(t => t.split('/')[1].toUpperCase()).join(', ')}`,
|
|
37195
|
+
};
|
|
37196
|
+
}
|
|
37197
|
+
// Check file size
|
|
37198
|
+
if (file.size > opts.maxSize) {
|
|
37199
|
+
const maxMB = Math.round(opts.maxSize / (1024 * 1024));
|
|
37200
|
+
return {
|
|
37201
|
+
valid: false,
|
|
37202
|
+
error: 'fileTooLarge',
|
|
37203
|
+
message: `La imagen es muy grande. Máximo ${maxMB}MB`,
|
|
37204
|
+
};
|
|
37205
|
+
}
|
|
37206
|
+
return { valid: true };
|
|
37207
|
+
}
|
|
37208
|
+
/**
|
|
37209
|
+
* Validate image dimensions (async - requires loading image)
|
|
37210
|
+
* @param file - File to validate
|
|
37211
|
+
* @param options - Validation options with minWidth/minHeight
|
|
37212
|
+
* @returns Promise with validation result
|
|
37213
|
+
*/
|
|
37214
|
+
async validateDimensions(file, options) {
|
|
37215
|
+
const img = await this.loadImage(file);
|
|
37216
|
+
if (options.minWidth && img.width < options.minWidth) {
|
|
37217
|
+
return {
|
|
37218
|
+
valid: false,
|
|
37219
|
+
error: 'imageTooSmall',
|
|
37220
|
+
message: `La imagen debe tener al menos ${options.minWidth}px de ancho`,
|
|
37221
|
+
};
|
|
37222
|
+
}
|
|
37223
|
+
if (options.minHeight && img.height < options.minHeight) {
|
|
37224
|
+
return {
|
|
37225
|
+
valid: false,
|
|
37226
|
+
error: 'imageTooSmall',
|
|
37227
|
+
message: `La imagen debe tener al menos ${options.minHeight}px de alto`,
|
|
37228
|
+
};
|
|
37229
|
+
}
|
|
37230
|
+
return { valid: true };
|
|
37231
|
+
}
|
|
37232
|
+
/**
|
|
37233
|
+
* Convert a Blob/File to a data URL
|
|
37234
|
+
*/
|
|
37235
|
+
async toDataUrl(file) {
|
|
37236
|
+
return new Promise((resolve, reject) => {
|
|
37237
|
+
const reader = new FileReader();
|
|
37238
|
+
reader.onload = () => resolve(reader.result);
|
|
37239
|
+
reader.onerror = reject;
|
|
37240
|
+
reader.readAsDataURL(file);
|
|
37241
|
+
});
|
|
37242
|
+
}
|
|
37243
|
+
/**
|
|
37244
|
+
* Convert a data URL to a Blob
|
|
37245
|
+
*/
|
|
37246
|
+
dataUrlToBlob(dataUrl) {
|
|
37247
|
+
const arr = dataUrl.split(',');
|
|
37248
|
+
const mime = arr[0].match(/:(.*?);/)[1];
|
|
37249
|
+
const bstr = atob(arr[1]);
|
|
37250
|
+
let n = bstr.length;
|
|
37251
|
+
const u8arr = new Uint8Array(n);
|
|
37252
|
+
while (n--) {
|
|
37253
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
37254
|
+
}
|
|
37255
|
+
return new Blob([u8arr], { type: mime });
|
|
37256
|
+
}
|
|
37257
|
+
// ============== Private Helpers ==============
|
|
37258
|
+
loadImage(file) {
|
|
37259
|
+
return new Promise((resolve, reject) => {
|
|
37260
|
+
const img = new Image();
|
|
37261
|
+
img.onload = () => {
|
|
37262
|
+
URL.revokeObjectURL(img.src);
|
|
37263
|
+
resolve(img);
|
|
37264
|
+
};
|
|
37265
|
+
img.onerror = reject;
|
|
37266
|
+
img.src = URL.createObjectURL(file);
|
|
37267
|
+
});
|
|
37268
|
+
}
|
|
37269
|
+
calculateDimensions(originalWidth, originalHeight, maxWidth, maxHeight) {
|
|
37270
|
+
let width = originalWidth;
|
|
37271
|
+
let height = originalHeight;
|
|
37272
|
+
// Scale down if necessary, maintaining aspect ratio
|
|
37273
|
+
if (width > maxWidth) {
|
|
37274
|
+
height = (height * maxWidth) / width;
|
|
37275
|
+
width = maxWidth;
|
|
37276
|
+
}
|
|
37277
|
+
if (height > maxHeight) {
|
|
37278
|
+
width = (width * maxHeight) / height;
|
|
37279
|
+
height = maxHeight;
|
|
37280
|
+
}
|
|
37281
|
+
return {
|
|
37282
|
+
width: Math.round(width),
|
|
37283
|
+
height: Math.round(height),
|
|
37284
|
+
};
|
|
37285
|
+
}
|
|
37286
|
+
canvasToBlob(canvas, mimeType, quality) {
|
|
37287
|
+
return new Promise((resolve, reject) => {
|
|
37288
|
+
canvas.toBlob((blob) => {
|
|
37289
|
+
if (blob)
|
|
37290
|
+
resolve(blob);
|
|
37291
|
+
else
|
|
37292
|
+
reject(new Error('Failed to create blob from canvas'));
|
|
37293
|
+
}, mimeType, quality);
|
|
37294
|
+
});
|
|
37295
|
+
}
|
|
37296
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
37297
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, providedIn: 'root' }); }
|
|
37298
|
+
}
|
|
37299
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, decorators: [{
|
|
37300
|
+
type: Injectable,
|
|
37301
|
+
args: [{ providedIn: 'root' }]
|
|
37302
|
+
}] });
|
|
37303
|
+
|
|
37050
37304
|
class AttachmentUploaderComponent {
|
|
37051
37305
|
get readyUrls() {
|
|
37052
37306
|
return this.attachments()
|
|
@@ -37069,6 +37323,7 @@ class AttachmentUploaderComponent {
|
|
|
37069
37323
|
this.attachmentsChange = output();
|
|
37070
37324
|
this.i18n = inject(I18nService);
|
|
37071
37325
|
this.feedbackService = inject(FeedbackService, { optional: true });
|
|
37326
|
+
this.imageService = inject(ImageService);
|
|
37072
37327
|
this.attachments = signal([]);
|
|
37073
37328
|
this.showCameraOverlay = signal(false);
|
|
37074
37329
|
this.cameraStream = null;
|
|
@@ -37170,6 +37425,35 @@ class AttachmentUploaderComponent {
|
|
|
37170
37425
|
});
|
|
37171
37426
|
});
|
|
37172
37427
|
}
|
|
37428
|
+
/**
|
|
37429
|
+
* Genera y sube la miniatura de una imagen ya comprimida.
|
|
37430
|
+
*
|
|
37431
|
+
* Recibe el archivo COMPRIMIDO a propósito: `compressorjs` ya normalizó la
|
|
37432
|
+
* orientación EXIF, y `ImageService` trabaja sobre canvas sin leer EXIF — si
|
|
37433
|
+
* se le pasara el original, las fotos verticales de teléfono saldrían
|
|
37434
|
+
* rotadas. Ver la nota en `generateThumbnail` (types.ts).
|
|
37435
|
+
*
|
|
37436
|
+
* Nunca es fatal: si falla, el adjunto queda subido sin miniatura y las
|
|
37437
|
+
* listas caen al archivo grande. Perder la miniatura no justifica perder la
|
|
37438
|
+
* foto que el usuario acaba de subir.
|
|
37439
|
+
*/
|
|
37440
|
+
async uploadThumbnail(compressed) {
|
|
37441
|
+
try {
|
|
37442
|
+
const size = this.props().thumbnailSize ?? 320;
|
|
37443
|
+
const thumb = await this.imageService.thumbnail(compressed, size);
|
|
37444
|
+
const baseName = compressed.name.replace(/\.[^.]+$/, '');
|
|
37445
|
+
const thumbFile = new File([thumb.blob], `${baseName}-thumb.jpg`, { type: 'image/jpeg' });
|
|
37446
|
+
const thumbUploadFn = this.props().thumbnailUploadFn ??
|
|
37447
|
+
this.props().uploadFn ??
|
|
37448
|
+
this.feedbackService?.uploadAttachment.bind(this.feedbackService);
|
|
37449
|
+
if (!thumbUploadFn)
|
|
37450
|
+
return undefined;
|
|
37451
|
+
return await thumbUploadFn(thumbFile);
|
|
37452
|
+
}
|
|
37453
|
+
catch {
|
|
37454
|
+
return undefined;
|
|
37455
|
+
}
|
|
37456
|
+
}
|
|
37173
37457
|
async uploadFile(id, file) {
|
|
37174
37458
|
try {
|
|
37175
37459
|
const shouldCompress = this.props().compressImages !== false && file.type.startsWith('image/');
|
|
@@ -37178,7 +37462,15 @@ class AttachmentUploaderComponent {
|
|
|
37178
37462
|
if (!uploadFn)
|
|
37179
37463
|
throw new Error('No upload function configured');
|
|
37180
37464
|
const url = await uploadFn(fileToUpload);
|
|
37181
|
-
|
|
37465
|
+
// La miniatura SIEMPRE sale de un archivo pasado por compressorjs, que es
|
|
37466
|
+
// lo que endereza la orientación EXIF. Si el consumer desactivó la
|
|
37467
|
+
// compresión, se comprime igual solo para generar la miniatura — el
|
|
37468
|
+
// archivo que se sube como principal sigue siendo el original.
|
|
37469
|
+
const wantsThumb = this.props().generateThumbnail === true && file.type.startsWith('image/');
|
|
37470
|
+
const thumbnailUrl = wantsThumb
|
|
37471
|
+
? await this.uploadThumbnail(shouldCompress ? fileToUpload : await this.compressImage(file))
|
|
37472
|
+
: undefined;
|
|
37473
|
+
this.attachments.update(list => list.map(a => (a.id === id ? { ...a, status: 'ready', url, thumbnailUrl } : a)));
|
|
37182
37474
|
}
|
|
37183
37475
|
catch {
|
|
37184
37476
|
const error = this.i18n.t('attachUploadFailed');
|
|
@@ -38719,7 +39011,7 @@ const SURVEY_RESPONSE_I18N = {
|
|
|
38719
39011
|
},
|
|
38720
39012
|
};
|
|
38721
39013
|
|
|
38722
|
-
const NAMESPACE$
|
|
39014
|
+
const NAMESPACE$4 = 'SurveyResponse';
|
|
38723
39015
|
/** Code del backend cuando la invitación de esa persona ya se respondió. */
|
|
38724
39016
|
const INVITE_USED_CODE = 'SURVEY_INVITE_ALREADY_USED';
|
|
38725
39017
|
/** Tipos cuya respuesta es un booleano, no un texto. */
|
|
@@ -38798,8 +39090,8 @@ class SurveyResponseComponent {
|
|
|
38798
39090
|
this.i18n.lang();
|
|
38799
39091
|
return { variant: 'error', title: this.t('loginRequiredTitle'), description: this.t('loginRequiredBody') };
|
|
38800
39092
|
});
|
|
38801
|
-
if (!this.i18n.hasNamespace(NAMESPACE$
|
|
38802
|
-
this.i18n.registerDefaults(NAMESPACE$
|
|
39093
|
+
if (!this.i18n.hasNamespace(NAMESPACE$4)) {
|
|
39094
|
+
this.i18n.registerDefaults(NAMESPACE$4, SURVEY_RESPONSE_I18N);
|
|
38803
39095
|
}
|
|
38804
39096
|
}
|
|
38805
39097
|
async ngOnInit() {
|
|
@@ -38989,12 +39281,12 @@ class SurveyResponseComponent {
|
|
|
38989
39281
|
this.errors.handle(err, {
|
|
38990
39282
|
context: 'surveyResponse.submit',
|
|
38991
39283
|
fallbackKey: 'sendError',
|
|
38992
|
-
i18nNamespace: NAMESPACE$
|
|
39284
|
+
i18nNamespace: NAMESPACE$4,
|
|
38993
39285
|
});
|
|
38994
39286
|
}
|
|
38995
39287
|
}
|
|
38996
39288
|
t(key) {
|
|
38997
|
-
return this.i18n.t(key, NAMESPACE$
|
|
39289
|
+
return this.i18n.t(key, NAMESPACE$4);
|
|
38998
39290
|
}
|
|
38999
39291
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyResponseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
39000
39292
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SurveyResponseComponent, isStandalone: true, selector: "val-survey-response", inputs: { props: "props" }, outputs: { submitted: "submitted" }, ngImport: i0, template: `
|
|
@@ -39603,7 +39895,7 @@ const FIELD_SCHEMA_EDITOR_I18N = {
|
|
|
39603
39895
|
},
|
|
39604
39896
|
};
|
|
39605
39897
|
|
|
39606
|
-
const NAMESPACE$
|
|
39898
|
+
const NAMESPACE$3 = 'FieldSchemaEditor';
|
|
39607
39899
|
/**
|
|
39608
39900
|
* Tipos cuyo valor sale de una lista cerrada que define quien arma el
|
|
39609
39901
|
* formulario — sin al menos una opción, el campo no se puede responder.
|
|
@@ -39688,8 +39980,8 @@ class FieldSchemaEditorComponent {
|
|
|
39688
39980
|
options: this.options(),
|
|
39689
39981
|
};
|
|
39690
39982
|
});
|
|
39691
|
-
if (!this.i18n.hasNamespace(NAMESPACE$
|
|
39692
|
-
this.i18n.registerDefaults(NAMESPACE$
|
|
39983
|
+
if (!this.i18n.hasNamespace(NAMESPACE$3)) {
|
|
39984
|
+
this.i18n.registerDefaults(NAMESPACE$3, FIELD_SCHEMA_EDITOR_I18N);
|
|
39693
39985
|
}
|
|
39694
39986
|
this.typeControl.valueChanges.subscribe(value => {
|
|
39695
39987
|
this.currentType.set(value || 'TEXT');
|
|
@@ -39726,7 +40018,7 @@ class FieldSchemaEditorComponent {
|
|
|
39726
40018
|
}
|
|
39727
40019
|
t(key) {
|
|
39728
40020
|
this.i18n.lang();
|
|
39729
|
-
return this.i18n.t(key, NAMESPACE$
|
|
40021
|
+
return this.i18n.t(key, NAMESPACE$3);
|
|
39730
40022
|
}
|
|
39731
40023
|
state() {
|
|
39732
40024
|
return this.props.state ?? ComponentStates.ENABLED;
|
|
@@ -39953,6 +40245,379 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
39953
40245
|
type: Output
|
|
39954
40246
|
}] } });
|
|
39955
40247
|
|
|
40248
|
+
const REQUEST_REVIEW_PANEL_I18N = {
|
|
40249
|
+
es: {
|
|
40250
|
+
approve: 'Aprobar',
|
|
40251
|
+
reject: 'Rechazar',
|
|
40252
|
+
cancel: 'Cancelar',
|
|
40253
|
+
confirmReject: 'Confirmar rechazo',
|
|
40254
|
+
rejectReason: 'Motivo del rechazo',
|
|
40255
|
+
rejectReasonPlaceholder: 'Cuéntale qué le faltó o qué debe corregir',
|
|
40256
|
+
rejectReasonHint: 'Esto le llega a quien envió la solicitud.',
|
|
40257
|
+
},
|
|
40258
|
+
en: {
|
|
40259
|
+
approve: 'Approve',
|
|
40260
|
+
reject: 'Reject',
|
|
40261
|
+
cancel: 'Cancel',
|
|
40262
|
+
confirmReject: 'Confirm rejection',
|
|
40263
|
+
rejectReason: 'Reason for rejection',
|
|
40264
|
+
rejectReasonPlaceholder: 'Tell them what was missing or needs fixing',
|
|
40265
|
+
rejectReasonHint: 'This is sent to whoever submitted the request.',
|
|
40266
|
+
},
|
|
40267
|
+
};
|
|
40268
|
+
|
|
40269
|
+
const NAMESPACE$2 = 'RequestReviewPanel';
|
|
40270
|
+
/**
|
|
40271
|
+
* val-request-review-panel — cola de revisión del factory (ADR-062).
|
|
40272
|
+
*
|
|
40273
|
+
* Es **presentacional**: no carga datos, no conoce el backend ni el dominio. El
|
|
40274
|
+
* consumer le pasa la cola ya normalizada y escucha `(decision)`.
|
|
40275
|
+
*
|
|
40276
|
+
* ## Por qué el cuerpo va proyectado y no configurado
|
|
40277
|
+
*
|
|
40278
|
+
* El ADR original proponía describir el cuerpo con arrays de nombres de campo
|
|
40279
|
+
* (`fields.display: ['taxId', 'tagsRequested']`). Al implementarlo con los dos
|
|
40280
|
+
* consumidores reales delante quedó claro que eso reinventa un motor de
|
|
40281
|
+
* templating peor que Angular: Chesed necesita una lista de documentos con
|
|
40282
|
+
* iconos y `target="_blank"`, Okhelia un enlace a la receta y un input de
|
|
40283
|
+
* comentario. Ninguna lista de strings expresa eso sin agregarle un flag al
|
|
40284
|
+
* componente por cada caso nuevo.
|
|
40285
|
+
*
|
|
40286
|
+
* Entonces el panel es dueño de lo que **de verdad** se repite — la cáscara, el
|
|
40287
|
+
* chrome de la card, y la mecánica de decisión — y el cuerpo de cada ítem lo
|
|
40288
|
+
* escribe cada vertical en un `ng-template` con su propio HTML.
|
|
40289
|
+
*
|
|
40290
|
+
* ```html
|
|
40291
|
+
* <val-request-review-panel [props]="panelProps()" [itemBody]="body"
|
|
40292
|
+
* (decision)="onDecision($event)" />
|
|
40293
|
+
*
|
|
40294
|
+
* <ng-template #body let-item>
|
|
40295
|
+
* <a [routerLink]="['/app/recetas', recipeIdOf(item)]">Ver receta</a>
|
|
40296
|
+
* </ng-template>
|
|
40297
|
+
* ```
|
|
40298
|
+
*
|
|
40299
|
+
* ## Rechazo con motivo
|
|
40300
|
+
*
|
|
40301
|
+
* El flujo de dos pasos (Rechazar → escribir motivo → Confirmar) es del panel,
|
|
40302
|
+
* no del consumer, y está activo por defecto. Rechazar de un click deja al
|
|
40303
|
+
* solicitante sin saber qué corregir; era el único de los dos paneles que lo
|
|
40304
|
+
* tenía bien resuelto y ahora lo heredan los dos.
|
|
40305
|
+
*/
|
|
40306
|
+
class RequestReviewPanelComponent {
|
|
40307
|
+
constructor() {
|
|
40308
|
+
this.i18n = inject(I18nService);
|
|
40309
|
+
this.props = input.required();
|
|
40310
|
+
this.decision = new EventEmitter();
|
|
40311
|
+
/** ID del ítem cuyo formulario de rechazo está abierto. */
|
|
40312
|
+
this._rejectingId = signal('');
|
|
40313
|
+
this.rejectingId = this._rejectingId.asReadonly();
|
|
40314
|
+
this._reason = signal('');
|
|
40315
|
+
this.reason = this._reason.asReadonly();
|
|
40316
|
+
this.cfg = computed(() => this.props());
|
|
40317
|
+
this.skeletonRows = computed(() => Array.from({ length: this.cfg().skeletonRows ?? 3 }, (_, i) => i));
|
|
40318
|
+
/** Hay una decisión en vuelo: toda la cola se congela. */
|
|
40319
|
+
this.isBusy = computed(() => !!this.cfg().busyItemId);
|
|
40320
|
+
if (!this.i18n.hasNamespace(NAMESPACE$2)) {
|
|
40321
|
+
this.i18n.registerContent(NAMESPACE$2, REQUEST_REVIEW_PANEL_I18N);
|
|
40322
|
+
}
|
|
40323
|
+
}
|
|
40324
|
+
t(key) {
|
|
40325
|
+
return this.i18n.t(key, NAMESPACE$2);
|
|
40326
|
+
}
|
|
40327
|
+
/** El ítem en vuelo muestra WORKING; los demás quedan deshabilitados. */
|
|
40328
|
+
stateFor(item) {
|
|
40329
|
+
const busyId = this.cfg().busyItemId;
|
|
40330
|
+
if (busyId === item.id)
|
|
40331
|
+
return 'WORKING';
|
|
40332
|
+
return busyId ? 'DISABLED' : 'ENABLED';
|
|
40333
|
+
}
|
|
40334
|
+
approveState(item) {
|
|
40335
|
+
return this.stateFor(item);
|
|
40336
|
+
}
|
|
40337
|
+
/**
|
|
40338
|
+
* Confirmar el rechazo exige motivo cuando `requireRejectReason` está activo
|
|
40339
|
+
* (el default). Es el freno que evita la notificación de rechazo vacía.
|
|
40340
|
+
*/
|
|
40341
|
+
confirmRejectState(item) {
|
|
40342
|
+
const state = this.stateFor(item);
|
|
40343
|
+
if (state !== 'ENABLED')
|
|
40344
|
+
return state;
|
|
40345
|
+
const needsReason = this.cfg().requireRejectReason !== false;
|
|
40346
|
+
return needsReason && this._reason().trim().length === 0 ? 'DISABLED' : 'ENABLED';
|
|
40347
|
+
}
|
|
40348
|
+
onReasonInput(event) {
|
|
40349
|
+
this._reason.set(event.target.value);
|
|
40350
|
+
}
|
|
40351
|
+
startReject(item) {
|
|
40352
|
+
// Sin motivo obligatorio el rechazo es directo: abrir un formulario que no
|
|
40353
|
+
// exige nada solo agrega un paso.
|
|
40354
|
+
if (this.cfg().requireRejectReason === false) {
|
|
40355
|
+
this.decision.emit({ itemId: item.id, approved: false });
|
|
40356
|
+
return;
|
|
40357
|
+
}
|
|
40358
|
+
this._reason.set('');
|
|
40359
|
+
this._rejectingId.set(item.id);
|
|
40360
|
+
}
|
|
40361
|
+
cancelReject() {
|
|
40362
|
+
this._rejectingId.set('');
|
|
40363
|
+
this._reason.set('');
|
|
40364
|
+
}
|
|
40365
|
+
confirmReject(item) {
|
|
40366
|
+
const reason = this._reason().trim();
|
|
40367
|
+
if (this.cfg().requireRejectReason !== false && reason.length === 0)
|
|
40368
|
+
return;
|
|
40369
|
+
this.decision.emit({ itemId: item.id, approved: false, reason: reason || undefined });
|
|
40370
|
+
this.cancelReject();
|
|
40371
|
+
}
|
|
40372
|
+
approve(item) {
|
|
40373
|
+
this.decision.emit({ itemId: item.id, approved: true });
|
|
40374
|
+
}
|
|
40375
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestReviewPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
40376
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestReviewPanelComponent, isStandalone: true, selector: "val-request-review-panel", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: true, transformFunction: null }, itemBody: { classPropertyName: "itemBody", publicName: "itemBody", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { decision: "decision" }, ngImport: i0, template: `
|
|
40377
|
+
<div class="rrp">
|
|
40378
|
+
@if (cfg().loading) {
|
|
40379
|
+
<div class="rrp__skel" aria-hidden="true">
|
|
40380
|
+
@for (row of skeletonRows(); track row) {
|
|
40381
|
+
<val-skeleton [props]="{ width: '100%', height: '120px', borderRadius: '14px' }" />
|
|
40382
|
+
}
|
|
40383
|
+
</div>
|
|
40384
|
+
} @else if (cfg().errorState) {
|
|
40385
|
+
<val-empty-state [props]="cfg().errorState!" />
|
|
40386
|
+
} @else if (cfg().items.length === 0) {
|
|
40387
|
+
@if (cfg().emptyState; as empty) {
|
|
40388
|
+
<val-empty-state [props]="empty" />
|
|
40389
|
+
}
|
|
40390
|
+
} @else {
|
|
40391
|
+
@if (cfg().countLabel; as count) {
|
|
40392
|
+
<p class="rrp__count">{{ count }}</p>
|
|
40393
|
+
}
|
|
40394
|
+
|
|
40395
|
+
<ul class="rrp__queue">
|
|
40396
|
+
@for (item of cfg().items; track item.id) {
|
|
40397
|
+
<li class="rrp__item">
|
|
40398
|
+
<h3 class="rrp__title">{{ item.title }}</h3>
|
|
40399
|
+
|
|
40400
|
+
@for (line of item.meta ?? []; track line) {
|
|
40401
|
+
<p class="rrp__meta">{{ line }}</p>
|
|
40402
|
+
}
|
|
40403
|
+
|
|
40404
|
+
@if (itemBody) {
|
|
40405
|
+
<div class="rrp__body">
|
|
40406
|
+
<ng-container
|
|
40407
|
+
[ngTemplateOutlet]="itemBody"
|
|
40408
|
+
[ngTemplateOutletContext]="{ $implicit: item, item: item }"
|
|
40409
|
+
/>
|
|
40410
|
+
</div>
|
|
40411
|
+
}
|
|
40412
|
+
|
|
40413
|
+
@if (rejectingId() === item.id) {
|
|
40414
|
+
<div class="rrp__reject">
|
|
40415
|
+
<label class="rrp__reject-label" [attr.for]="'rrp-note-' + item.id">
|
|
40416
|
+
{{ cfg().rejectReasonLabel ?? t('rejectReason') }}
|
|
40417
|
+
</label>
|
|
40418
|
+
<textarea
|
|
40419
|
+
class="rrp__reject-input"
|
|
40420
|
+
[id]="'rrp-note-' + item.id"
|
|
40421
|
+
rows="3"
|
|
40422
|
+
[placeholder]="cfg().rejectReasonPlaceholder ?? t('rejectReasonPlaceholder')"
|
|
40423
|
+
[value]="reason()"
|
|
40424
|
+
(input)="onReasonInput($event)"
|
|
40425
|
+
></textarea>
|
|
40426
|
+
<p class="rrp__reject-hint">
|
|
40427
|
+
{{ cfg().rejectReasonHint ?? t('rejectReasonHint') }}
|
|
40428
|
+
</p>
|
|
40429
|
+
|
|
40430
|
+
<div class="rrp__actions">
|
|
40431
|
+
<val-button
|
|
40432
|
+
[props]="{
|
|
40433
|
+
token: 'rrp-cancel-' + item.id,
|
|
40434
|
+
text: cfg().cancelLabel ?? t('cancel'),
|
|
40435
|
+
color: 'dark',
|
|
40436
|
+
fill: 'clear',
|
|
40437
|
+
size: 'small',
|
|
40438
|
+
type: 'button',
|
|
40439
|
+
state: isBusy() ? 'DISABLED' : 'ENABLED',
|
|
40440
|
+
}"
|
|
40441
|
+
(onClick)="cancelReject()"
|
|
40442
|
+
/>
|
|
40443
|
+
<val-button
|
|
40444
|
+
[props]="{
|
|
40445
|
+
token: 'rrp-confirm-reject-' + item.id,
|
|
40446
|
+
text: cfg().confirmRejectLabel ?? t('confirmReject'),
|
|
40447
|
+
color: 'danger',
|
|
40448
|
+
fill: 'solid',
|
|
40449
|
+
shape: 'round',
|
|
40450
|
+
size: 'small',
|
|
40451
|
+
type: 'button',
|
|
40452
|
+
state: confirmRejectState(item),
|
|
40453
|
+
}"
|
|
40454
|
+
(onClick)="confirmReject(item)"
|
|
40455
|
+
/>
|
|
40456
|
+
</div>
|
|
40457
|
+
</div>
|
|
40458
|
+
} @else {
|
|
40459
|
+
<div class="rrp__actions">
|
|
40460
|
+
<val-button
|
|
40461
|
+
[props]="{
|
|
40462
|
+
token: 'rrp-reject-' + item.id,
|
|
40463
|
+
text: cfg().rejectLabel ?? t('reject'),
|
|
40464
|
+
color: 'dark',
|
|
40465
|
+
fill: 'outline',
|
|
40466
|
+
shape: 'round',
|
|
40467
|
+
size: 'small',
|
|
40468
|
+
type: 'button',
|
|
40469
|
+
state: isBusy() ? 'DISABLED' : 'ENABLED',
|
|
40470
|
+
}"
|
|
40471
|
+
(onClick)="startReject(item)"
|
|
40472
|
+
/>
|
|
40473
|
+
<val-button
|
|
40474
|
+
[props]="{
|
|
40475
|
+
token: 'rrp-approve-' + item.id,
|
|
40476
|
+
text: cfg().approveLabel ?? t('approve'),
|
|
40477
|
+
color: 'primary',
|
|
40478
|
+
fill: 'solid',
|
|
40479
|
+
shape: 'round',
|
|
40480
|
+
size: 'small',
|
|
40481
|
+
type: 'button',
|
|
40482
|
+
state: approveState(item),
|
|
40483
|
+
}"
|
|
40484
|
+
(onClick)="approve(item)"
|
|
40485
|
+
/>
|
|
40486
|
+
</div>
|
|
40487
|
+
}
|
|
40488
|
+
</li>
|
|
40489
|
+
}
|
|
40490
|
+
</ul>
|
|
40491
|
+
}
|
|
40492
|
+
</div>
|
|
40493
|
+
`, isInline: true, styles: [":host{display:block}.rrp{display:flex;flex-direction:column;gap:12px}.rrp__skel{display:flex;flex-direction:column;gap:10px}.rrp__count{margin:0;font-size:.8125rem;font-weight:700;color:var(--ion-color-medium, #92949c);font-variant-numeric:tabular-nums}.rrp__queue{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.rrp__item{display:flex;flex-direction:column;gap:8px;padding:14px 16px;border-radius:14px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.rrp__title{margin:0;font-size:1rem;font-weight:700;color:var(--ion-text-color, #000)}.rrp__meta{margin:0;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.rrp__body{display:flex;flex-direction:column;gap:8px}.rrp__reject{display:flex;flex-direction:column;gap:6px}.rrp__reject-label{font-size:.8125rem;font-weight:600;color:var(--ion-text-color, #000)}.rrp__reject-input{width:100%;box-sizing:border-box;padding:10px 12px;border-radius:10px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-background-color, #fff);color:var(--ion-text-color, #000);font-family:inherit;font-size:.875rem;resize:vertical}.rrp__reject-input:focus{outline:none;border-color:var(--ion-color-primary)}.rrp__reject-hint{margin:0;font-size:.75rem;color:var(--ion-color-medium, #92949c)}.rrp__actions{display:flex;flex-direction:column;gap:8px;margin-top:4px}@media (min-width: 576px){.rrp__actions{flex-direction:row;justify-content:flex-end;align-items:center;flex-wrap:wrap}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SkeletonComponent, selector: "val-skeleton", inputs: ["props"] }] }); }
|
|
40494
|
+
}
|
|
40495
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestReviewPanelComponent, decorators: [{
|
|
40496
|
+
type: Component,
|
|
40497
|
+
args: [{ selector: 'val-request-review-panel', standalone: true, imports: [CommonModule, ButtonComponent, EmptyStateComponent, SkeletonComponent], template: `
|
|
40498
|
+
<div class="rrp">
|
|
40499
|
+
@if (cfg().loading) {
|
|
40500
|
+
<div class="rrp__skel" aria-hidden="true">
|
|
40501
|
+
@for (row of skeletonRows(); track row) {
|
|
40502
|
+
<val-skeleton [props]="{ width: '100%', height: '120px', borderRadius: '14px' }" />
|
|
40503
|
+
}
|
|
40504
|
+
</div>
|
|
40505
|
+
} @else if (cfg().errorState) {
|
|
40506
|
+
<val-empty-state [props]="cfg().errorState!" />
|
|
40507
|
+
} @else if (cfg().items.length === 0) {
|
|
40508
|
+
@if (cfg().emptyState; as empty) {
|
|
40509
|
+
<val-empty-state [props]="empty" />
|
|
40510
|
+
}
|
|
40511
|
+
} @else {
|
|
40512
|
+
@if (cfg().countLabel; as count) {
|
|
40513
|
+
<p class="rrp__count">{{ count }}</p>
|
|
40514
|
+
}
|
|
40515
|
+
|
|
40516
|
+
<ul class="rrp__queue">
|
|
40517
|
+
@for (item of cfg().items; track item.id) {
|
|
40518
|
+
<li class="rrp__item">
|
|
40519
|
+
<h3 class="rrp__title">{{ item.title }}</h3>
|
|
40520
|
+
|
|
40521
|
+
@for (line of item.meta ?? []; track line) {
|
|
40522
|
+
<p class="rrp__meta">{{ line }}</p>
|
|
40523
|
+
}
|
|
40524
|
+
|
|
40525
|
+
@if (itemBody) {
|
|
40526
|
+
<div class="rrp__body">
|
|
40527
|
+
<ng-container
|
|
40528
|
+
[ngTemplateOutlet]="itemBody"
|
|
40529
|
+
[ngTemplateOutletContext]="{ $implicit: item, item: item }"
|
|
40530
|
+
/>
|
|
40531
|
+
</div>
|
|
40532
|
+
}
|
|
40533
|
+
|
|
40534
|
+
@if (rejectingId() === item.id) {
|
|
40535
|
+
<div class="rrp__reject">
|
|
40536
|
+
<label class="rrp__reject-label" [attr.for]="'rrp-note-' + item.id">
|
|
40537
|
+
{{ cfg().rejectReasonLabel ?? t('rejectReason') }}
|
|
40538
|
+
</label>
|
|
40539
|
+
<textarea
|
|
40540
|
+
class="rrp__reject-input"
|
|
40541
|
+
[id]="'rrp-note-' + item.id"
|
|
40542
|
+
rows="3"
|
|
40543
|
+
[placeholder]="cfg().rejectReasonPlaceholder ?? t('rejectReasonPlaceholder')"
|
|
40544
|
+
[value]="reason()"
|
|
40545
|
+
(input)="onReasonInput($event)"
|
|
40546
|
+
></textarea>
|
|
40547
|
+
<p class="rrp__reject-hint">
|
|
40548
|
+
{{ cfg().rejectReasonHint ?? t('rejectReasonHint') }}
|
|
40549
|
+
</p>
|
|
40550
|
+
|
|
40551
|
+
<div class="rrp__actions">
|
|
40552
|
+
<val-button
|
|
40553
|
+
[props]="{
|
|
40554
|
+
token: 'rrp-cancel-' + item.id,
|
|
40555
|
+
text: cfg().cancelLabel ?? t('cancel'),
|
|
40556
|
+
color: 'dark',
|
|
40557
|
+
fill: 'clear',
|
|
40558
|
+
size: 'small',
|
|
40559
|
+
type: 'button',
|
|
40560
|
+
state: isBusy() ? 'DISABLED' : 'ENABLED',
|
|
40561
|
+
}"
|
|
40562
|
+
(onClick)="cancelReject()"
|
|
40563
|
+
/>
|
|
40564
|
+
<val-button
|
|
40565
|
+
[props]="{
|
|
40566
|
+
token: 'rrp-confirm-reject-' + item.id,
|
|
40567
|
+
text: cfg().confirmRejectLabel ?? t('confirmReject'),
|
|
40568
|
+
color: 'danger',
|
|
40569
|
+
fill: 'solid',
|
|
40570
|
+
shape: 'round',
|
|
40571
|
+
size: 'small',
|
|
40572
|
+
type: 'button',
|
|
40573
|
+
state: confirmRejectState(item),
|
|
40574
|
+
}"
|
|
40575
|
+
(onClick)="confirmReject(item)"
|
|
40576
|
+
/>
|
|
40577
|
+
</div>
|
|
40578
|
+
</div>
|
|
40579
|
+
} @else {
|
|
40580
|
+
<div class="rrp__actions">
|
|
40581
|
+
<val-button
|
|
40582
|
+
[props]="{
|
|
40583
|
+
token: 'rrp-reject-' + item.id,
|
|
40584
|
+
text: cfg().rejectLabel ?? t('reject'),
|
|
40585
|
+
color: 'dark',
|
|
40586
|
+
fill: 'outline',
|
|
40587
|
+
shape: 'round',
|
|
40588
|
+
size: 'small',
|
|
40589
|
+
type: 'button',
|
|
40590
|
+
state: isBusy() ? 'DISABLED' : 'ENABLED',
|
|
40591
|
+
}"
|
|
40592
|
+
(onClick)="startReject(item)"
|
|
40593
|
+
/>
|
|
40594
|
+
<val-button
|
|
40595
|
+
[props]="{
|
|
40596
|
+
token: 'rrp-approve-' + item.id,
|
|
40597
|
+
text: cfg().approveLabel ?? t('approve'),
|
|
40598
|
+
color: 'primary',
|
|
40599
|
+
fill: 'solid',
|
|
40600
|
+
shape: 'round',
|
|
40601
|
+
size: 'small',
|
|
40602
|
+
type: 'button',
|
|
40603
|
+
state: approveState(item),
|
|
40604
|
+
}"
|
|
40605
|
+
(onClick)="approve(item)"
|
|
40606
|
+
/>
|
|
40607
|
+
</div>
|
|
40608
|
+
}
|
|
40609
|
+
</li>
|
|
40610
|
+
}
|
|
40611
|
+
</ul>
|
|
40612
|
+
}
|
|
40613
|
+
</div>
|
|
40614
|
+
`, styles: [":host{display:block}.rrp{display:flex;flex-direction:column;gap:12px}.rrp__skel{display:flex;flex-direction:column;gap:10px}.rrp__count{margin:0;font-size:.8125rem;font-weight:700;color:var(--ion-color-medium, #92949c);font-variant-numeric:tabular-nums}.rrp__queue{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.rrp__item{display:flex;flex-direction:column;gap:8px;padding:14px 16px;border-radius:14px;background:var(--ion-card-background, var(--ion-background-color, #fff));border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.rrp__title{margin:0;font-size:1rem;font-weight:700;color:var(--ion-text-color, #000)}.rrp__meta{margin:0;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.rrp__body{display:flex;flex-direction:column;gap:8px}.rrp__reject{display:flex;flex-direction:column;gap:6px}.rrp__reject-label{font-size:.8125rem;font-weight:600;color:var(--ion-text-color, #000)}.rrp__reject-input{width:100%;box-sizing:border-box;padding:10px 12px;border-radius:10px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-background-color, #fff);color:var(--ion-text-color, #000);font-family:inherit;font-size:.875rem;resize:vertical}.rrp__reject-input:focus{outline:none;border-color:var(--ion-color-primary)}.rrp__reject-hint{margin:0;font-size:.75rem;color:var(--ion-color-medium, #92949c)}.rrp__actions{display:flex;flex-direction:column;gap:8px;margin-top:4px}@media (min-width: 576px){.rrp__actions{flex-direction:row;justify-content:flex-end;align-items:center;flex-wrap:wrap}}\n"] }]
|
|
40615
|
+
}], ctorParameters: () => [], propDecorators: { itemBody: [{
|
|
40616
|
+
type: Input
|
|
40617
|
+
}], decision: [{
|
|
40618
|
+
type: Output
|
|
40619
|
+
}] } });
|
|
40620
|
+
|
|
39956
40621
|
class RequestFormBuilderService extends FormSchemaBuilderService {
|
|
39957
40622
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestFormBuilderService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
39958
40623
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestFormBuilderService, providedIn: 'root' }); }
|
|
@@ -40074,6 +40739,13 @@ class SurveyBuilderComponent {
|
|
|
40074
40739
|
constructor() {
|
|
40075
40740
|
this.props = {};
|
|
40076
40741
|
this.saved = new EventEmitter();
|
|
40742
|
+
/**
|
|
40743
|
+
* Emite lo escrito, ya validado, SIN guardarlo. Solo con `props.persist:
|
|
40744
|
+
* false` — ver el comentario de ese campo: cuando la app tiene endpoint
|
|
40745
|
+
* propio, guardar desde acá se salta la validación de la entidad dueña y el
|
|
40746
|
+
* permiso de la vertical.
|
|
40747
|
+
*/
|
|
40748
|
+
this.draft = new EventEmitter();
|
|
40077
40749
|
this.i18n = inject(I18nService);
|
|
40078
40750
|
this.requests = inject(RequestService);
|
|
40079
40751
|
this.formBuilder = inject(RequestFormBuilderService);
|
|
@@ -40235,7 +40907,6 @@ class SurveyBuilderComponent {
|
|
|
40235
40907
|
this.validationError.set(this.t('missingOptions'));
|
|
40236
40908
|
return;
|
|
40237
40909
|
}
|
|
40238
|
-
this.saving.set(true);
|
|
40239
40910
|
const fieldSchema = [];
|
|
40240
40911
|
rows.forEach((row, index) => {
|
|
40241
40912
|
const name = this.formBuilder.uniqueFieldName(row.name || row.label, fieldSchema);
|
|
@@ -40249,8 +40920,19 @@ class SurveyBuilderComponent {
|
|
|
40249
40920
|
options: row.options,
|
|
40250
40921
|
}, index));
|
|
40251
40922
|
});
|
|
40252
|
-
const typeId = this.props.typeId ?? `${this.props.typePrefix ?? DEFAULT_TYPE_PREFIX}:${this.formBuilder.toToken(title)}`;
|
|
40253
40923
|
const subtitle = this.subtitleControl.value.trim();
|
|
40924
|
+
// La app guarda: acá solo se valida y se entrega lo escrito.
|
|
40925
|
+
if (this.props.persist === false) {
|
|
40926
|
+
this.draft.emit({ label: title, subtitle, questions: fieldSchema });
|
|
40927
|
+
return;
|
|
40928
|
+
}
|
|
40929
|
+
this.saving.set(true);
|
|
40930
|
+
// `typeId` derivado del título: sirve para un tipo de solicitud de
|
|
40931
|
+
// plataforma, donde el título ES la identidad. Para una encuesta atada a
|
|
40932
|
+
// una entidad NO alcanza — dos eventos con el mismo título colisionarían en
|
|
40933
|
+
// el mismo tipo y compartirían respuestas. Por eso una vertical usa su
|
|
40934
|
+
// endpoint (`persist: false`) y arma el id con la entidad adentro.
|
|
40935
|
+
const typeId = this.props.typeId ?? `${this.props.typePrefix ?? DEFAULT_TYPE_PREFIX}:${this.formBuilder.toToken(title)}`;
|
|
40254
40936
|
const payload = {
|
|
40255
40937
|
label: title,
|
|
40256
40938
|
subtitle,
|
|
@@ -40291,7 +40973,7 @@ class SurveyBuilderComponent {
|
|
|
40291
40973
|
};
|
|
40292
40974
|
}
|
|
40293
40975
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
40294
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SurveyBuilderComponent, isStandalone: true, selector: "val-survey-builder", inputs: { props: "props" }, outputs: { saved: "saved" }, ngImport: i0, template: `
|
|
40976
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SurveyBuilderComponent, isStandalone: true, selector: "val-survey-builder", inputs: { props: "props" }, outputs: { saved: "saved", draft: "draft" }, ngImport: i0, template: `
|
|
40295
40977
|
<div class="survey-builder">
|
|
40296
40978
|
<val-form-field [label]="t('titleLabel')">
|
|
40297
40979
|
<val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
|
|
@@ -40523,6 +41205,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
40523
41205
|
type: Input
|
|
40524
41206
|
}], saved: [{
|
|
40525
41207
|
type: Output
|
|
41208
|
+
}], draft: [{
|
|
41209
|
+
type: Output
|
|
40526
41210
|
}] } });
|
|
40527
41211
|
|
|
40528
41212
|
/**
|
|
@@ -44999,521 +45683,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
44999
45683
|
type: Output
|
|
45000
45684
|
}] } });
|
|
45001
45685
|
|
|
45002
|
-
const STATUS_STYLE = {
|
|
45003
|
-
approved: { bg: '#22c55e', color: '#fff' },
|
|
45004
|
-
rejected: { bg: '#ef4444', color: '#fff' },
|
|
45005
|
-
in_review: { bg: '#fef3c7', color: '#92400e' },
|
|
45006
|
-
pending: { bg: 'var(--ion-color-light, #f4f5f8)', color: 'var(--ion-color-dark, #222)' },
|
|
45007
|
-
cancelled: { bg: 'var(--ion-color-light, #f4f5f8)', color: 'var(--ion-color-medium, #636469)' },
|
|
45008
|
-
closed: { bg: 'var(--ion-color-light, #f4f5f8)', color: 'var(--ion-color-medium, #636469)' },
|
|
45009
|
-
};
|
|
45010
|
-
/**
|
|
45011
|
-
* Panel de revisión de una Request genérica (ADR-061/062): status + acciones
|
|
45012
|
-
* approve/reject + fields del formulario + comentarios. Config-driven, sin
|
|
45013
|
-
* conocimiento del dominio (adoption, recipe_verification, etc.) — cada
|
|
45014
|
-
* consumer inyecta su `RequestReviewPanelConfig`.
|
|
45015
|
-
*/
|
|
45016
|
-
class RequestReviewPanelComponent {
|
|
45017
|
-
constructor() {
|
|
45018
|
-
this.requests = inject(RequestService);
|
|
45019
|
-
this.i18n = inject(I18nService);
|
|
45020
|
-
this.errors = inject(ValtechErrorService);
|
|
45021
|
-
this.toast = inject(ToastService);
|
|
45022
|
-
this.destroyRef = inject(DestroyRef);
|
|
45023
|
-
this.requestId = input.required();
|
|
45024
|
-
this.config = input.required();
|
|
45025
|
-
this.loaded = output();
|
|
45026
|
-
this._req = signal(null);
|
|
45027
|
-
this._loading = signal(true);
|
|
45028
|
-
this._loadError = signal(null);
|
|
45029
|
-
this._comments = signal([]);
|
|
45030
|
-
this._loadingComments = signal(false);
|
|
45031
|
-
this._transitioning = signal(false);
|
|
45032
|
-
this.req = this._req.asReadonly();
|
|
45033
|
-
this.loading = this._loading.asReadonly();
|
|
45034
|
-
this.loadError = this._loadError.asReadonly();
|
|
45035
|
-
this.comments = this._comments.asReadonly();
|
|
45036
|
-
this.loadingComments = this._loadingComments.asReadonly();
|
|
45037
|
-
this.transitioning = this._transitioning.asReadonly();
|
|
45038
|
-
this.heroImgFailed = signal(false);
|
|
45039
|
-
this.heroConfig = computed(() => this.config().entityHero);
|
|
45040
|
-
this.statusStyle = computed(() => STATUS_STYLE[this._req()?.status ?? ''] ?? STATUS_STYLE['pending']);
|
|
45041
|
-
this.heroName = computed(() => {
|
|
45042
|
-
const hero = this.heroConfig();
|
|
45043
|
-
if (!hero)
|
|
45044
|
-
return undefined;
|
|
45045
|
-
return this._req()?.metadata?.[hero.nameField];
|
|
45046
|
-
});
|
|
45047
|
-
this.heroImage = computed(() => {
|
|
45048
|
-
const hero = this.heroConfig();
|
|
45049
|
-
if (!hero?.imageField)
|
|
45050
|
-
return undefined;
|
|
45051
|
-
return this._req()?.metadata?.[hero.imageField];
|
|
45052
|
-
});
|
|
45053
|
-
this.canNavigateHero = computed(() => {
|
|
45054
|
-
const hero = this.heroConfig();
|
|
45055
|
-
const metadata = this._req()?.metadata;
|
|
45056
|
-
if (!hero || !metadata)
|
|
45057
|
-
return false;
|
|
45058
|
-
return hero.canNavigate(metadata);
|
|
45059
|
-
});
|
|
45060
|
-
this.submitterInitial = computed(() => {
|
|
45061
|
-
const s = this._req()?.submitter;
|
|
45062
|
-
if (!s)
|
|
45063
|
-
return '?';
|
|
45064
|
-
return ((s.name || s.email) ?? '?').charAt(0).toUpperCase();
|
|
45065
|
-
});
|
|
45066
|
-
this.fieldEntries = computed(() => {
|
|
45067
|
-
const fields = this._req()?.fields ?? {};
|
|
45068
|
-
return Object.entries(fields)
|
|
45069
|
-
.filter(([, v]) => v != null && v !== '')
|
|
45070
|
-
.map(([k, v]) => ({ key: k, value: String(v) }));
|
|
45071
|
-
});
|
|
45072
|
-
this.errorState = computed(() => {
|
|
45073
|
-
this.i18n.lang();
|
|
45074
|
-
const err = this._loadError();
|
|
45075
|
-
const msg = (err instanceof Error ? err.message : String(err ?? '')).toLowerCase();
|
|
45076
|
-
const isOffline = msg.includes('network') || msg.includes('offline');
|
|
45077
|
-
return {
|
|
45078
|
-
variant: 'error',
|
|
45079
|
-
title: isOffline ? this.t('offlineTitle') : this.t('errorTitle'),
|
|
45080
|
-
description: isOffline ? this.t('offlineHint') : '',
|
|
45081
|
-
cta: { label: this.t('retry'), handler: () => void this.load() },
|
|
45082
|
-
};
|
|
45083
|
-
});
|
|
45084
|
-
}
|
|
45085
|
-
ngOnInit() {
|
|
45086
|
-
void this.load();
|
|
45087
|
-
}
|
|
45088
|
-
async load() {
|
|
45089
|
-
const id = this.requestId();
|
|
45090
|
-
this._loading.set(true);
|
|
45091
|
-
this._loadError.set(null);
|
|
45092
|
-
try {
|
|
45093
|
-
const req = await firstValueFrom(this.requests.getRequest(id));
|
|
45094
|
-
this._req.set(req);
|
|
45095
|
-
this.loaded.emit(req);
|
|
45096
|
-
this.subscribeComments(req.appId, req.orgId, id);
|
|
45097
|
-
}
|
|
45098
|
-
catch (err) {
|
|
45099
|
-
this._loadError.set(err);
|
|
45100
|
-
this.errors.handle(err, {
|
|
45101
|
-
context: 'request-review-panel.load',
|
|
45102
|
-
fallbackKey: 'errorTitle',
|
|
45103
|
-
i18nNamespace: this.config().i18nNamespace,
|
|
45104
|
-
});
|
|
45105
|
-
}
|
|
45106
|
-
finally {
|
|
45107
|
-
this._loading.set(false);
|
|
45108
|
-
}
|
|
45109
|
-
}
|
|
45110
|
-
subscribeComments(appId, orgId, requestId) {
|
|
45111
|
-
this._loadingComments.set(true);
|
|
45112
|
-
this.requests
|
|
45113
|
-
.watchComments(appId, orgId, requestId)
|
|
45114
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
45115
|
-
.subscribe({
|
|
45116
|
-
next: comments => {
|
|
45117
|
-
this._comments.set(comments);
|
|
45118
|
-
this._loadingComments.set(false);
|
|
45119
|
-
},
|
|
45120
|
-
error: () => {
|
|
45121
|
-
// comentarios son best-effort; si el listener falla, silenciar y ocultar spinner
|
|
45122
|
-
this._loadingComments.set(false);
|
|
45123
|
-
},
|
|
45124
|
-
});
|
|
45125
|
-
}
|
|
45126
|
-
async transition(status) {
|
|
45127
|
-
const req = this._req();
|
|
45128
|
-
if (!req || this._transitioning())
|
|
45129
|
-
return;
|
|
45130
|
-
this._transitioning.set(true);
|
|
45131
|
-
try {
|
|
45132
|
-
await firstValueFrom(this.requests.transition(req.id, { status }));
|
|
45133
|
-
await this.load();
|
|
45134
|
-
this.toast.show({ message: this.t('statusUpdated'), duration: 2500, color: 'dark', position: 'top' });
|
|
45135
|
-
}
|
|
45136
|
-
catch (err) {
|
|
45137
|
-
this.errors.handle(err, {
|
|
45138
|
-
context: 'request-review-panel.transition',
|
|
45139
|
-
fallbackKey: 'statusError',
|
|
45140
|
-
i18nNamespace: this.config().i18nNamespace,
|
|
45141
|
-
});
|
|
45142
|
-
}
|
|
45143
|
-
finally {
|
|
45144
|
-
this._transitioning.set(false);
|
|
45145
|
-
}
|
|
45146
|
-
}
|
|
45147
|
-
onHeroClick() {
|
|
45148
|
-
const hero = this.heroConfig();
|
|
45149
|
-
const metadata = this._req()?.metadata;
|
|
45150
|
-
if (hero && metadata)
|
|
45151
|
-
hero.navigate(metadata);
|
|
45152
|
-
}
|
|
45153
|
-
formatDate(iso) {
|
|
45154
|
-
if (!iso)
|
|
45155
|
-
return '';
|
|
45156
|
-
try {
|
|
45157
|
-
return new Date(iso).toLocaleDateString(this.i18n.lang() === 'es' ? 'es-CL' : 'en-US', {
|
|
45158
|
-
day: 'numeric',
|
|
45159
|
-
month: 'short',
|
|
45160
|
-
year: 'numeric',
|
|
45161
|
-
});
|
|
45162
|
-
}
|
|
45163
|
-
catch {
|
|
45164
|
-
return iso;
|
|
45165
|
-
}
|
|
45166
|
-
}
|
|
45167
|
-
authorInitial(c) {
|
|
45168
|
-
return ((c.author.name || c.author.email) ?? '?').charAt(0).toUpperCase();
|
|
45169
|
-
}
|
|
45170
|
-
relativeTime(iso) {
|
|
45171
|
-
if (!iso)
|
|
45172
|
-
return '';
|
|
45173
|
-
const diffMs = Date.now() - new Date(iso).getTime();
|
|
45174
|
-
if (Number.isNaN(diffMs))
|
|
45175
|
-
return iso;
|
|
45176
|
-
const lang = this.i18n.lang();
|
|
45177
|
-
const mins = Math.floor(diffMs / 60000);
|
|
45178
|
-
if (mins < 1)
|
|
45179
|
-
return lang === 'es' ? 'hace un momento' : 'just now';
|
|
45180
|
-
if (mins < 60)
|
|
45181
|
-
return lang === 'es' ? `hace ${mins} min` : `${mins}m ago`;
|
|
45182
|
-
const hours = Math.floor(mins / 60);
|
|
45183
|
-
if (hours < 24)
|
|
45184
|
-
return lang === 'es' ? `hace ${hours} h` : `${hours}h ago`;
|
|
45185
|
-
const days = Math.floor(hours / 24);
|
|
45186
|
-
if (days < 7)
|
|
45187
|
-
return lang === 'es' ? `hace ${days} dia${days > 1 ? 's' : ''}` : `${days}d ago`;
|
|
45188
|
-
return this.formatDate(iso);
|
|
45189
|
-
}
|
|
45190
|
-
t(key) {
|
|
45191
|
-
return this.i18n.t(key, this.config().i18nNamespace);
|
|
45192
|
-
}
|
|
45193
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestReviewPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
45194
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestReviewPanelComponent, isStandalone: true, selector: "val-request-review-panel", inputs: { requestId: { classPropertyName: "requestId", publicName: "requestId", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { loaded: "loaded" }, ngImport: i0, template: `
|
|
45195
|
-
<div class="page">
|
|
45196
|
-
@if (loading()) {
|
|
45197
|
-
<div class="skel" aria-hidden="true">
|
|
45198
|
-
<val-skeleton [props]="{ width: '100%', height: '88px', borderRadius: '14px' }" />
|
|
45199
|
-
<val-skeleton [props]="{ width: '100%', height: '64px', borderRadius: '12px' }" />
|
|
45200
|
-
<val-skeleton [props]="{ width: '100%', height: '80px', borderRadius: '12px' }" />
|
|
45201
|
-
<val-skeleton [props]="{ width: '100%', height: '120px', borderRadius: '12px' }" />
|
|
45202
|
-
</div>
|
|
45203
|
-
} @else if (loadError()) {
|
|
45204
|
-
<val-empty-state [props]="errorState()" />
|
|
45205
|
-
} @else if (req()) {
|
|
45206
|
-
<div class="detail">
|
|
45207
|
-
@if (heroConfig() && heroName()) {
|
|
45208
|
-
<button class="entity-hero" (click)="onHeroClick()" [disabled]="!canNavigateHero()">
|
|
45209
|
-
<div class="entity-hero__media">
|
|
45210
|
-
@if (heroImage() && !heroImgFailed()) {
|
|
45211
|
-
<img
|
|
45212
|
-
[src]="heroImage()"
|
|
45213
|
-
[alt]="heroName()!"
|
|
45214
|
-
loading="lazy"
|
|
45215
|
-
(error)="heroImgFailed.set(true)"
|
|
45216
|
-
class="entity-hero__img"
|
|
45217
|
-
/>
|
|
45218
|
-
} @else {
|
|
45219
|
-
<span class="entity-hero__placeholder" aria-hidden="true">{{
|
|
45220
|
-
heroConfig()!.placeholderIcon || '📄'
|
|
45221
|
-
}}</span>
|
|
45222
|
-
}
|
|
45223
|
-
</div>
|
|
45224
|
-
<div class="entity-hero__body">
|
|
45225
|
-
<span class="entity-hero__label">{{ t(heroConfig()!.labelKey) }}</span>
|
|
45226
|
-
<span class="entity-hero__name">{{ heroName() }}</span>
|
|
45227
|
-
</div>
|
|
45228
|
-
@if (canNavigateHero()) {
|
|
45229
|
-
<span class="entity-hero__chevron" aria-hidden="true">›</span>
|
|
45230
|
-
}
|
|
45231
|
-
</button>
|
|
45232
|
-
}
|
|
45233
|
-
|
|
45234
|
-
<div class="status-card" [style.border-left-color]="statusStyle().bg">
|
|
45235
|
-
<div class="status-card__top">
|
|
45236
|
-
<span class="status-badge" [style.background]="statusStyle().bg" [style.color]="statusStyle().color">
|
|
45237
|
-
{{ t('status_' + req()!.status) }}
|
|
45238
|
-
</span>
|
|
45239
|
-
<span class="submitted-at">{{ t('submittedAt') }}: {{ formatDate(req()!.createdAt) }}</span>
|
|
45240
|
-
</div>
|
|
45241
|
-
|
|
45242
|
-
@if (config().canReview() && (req()!.status === 'pending' || req()!.status === 'in_review')) {
|
|
45243
|
-
<div class="actions-row">
|
|
45244
|
-
<val-button
|
|
45245
|
-
[props]="{
|
|
45246
|
-
token: 'approve',
|
|
45247
|
-
text: t('approve'),
|
|
45248
|
-
color: 'primary',
|
|
45249
|
-
fill: 'solid',
|
|
45250
|
-
shape: 'round',
|
|
45251
|
-
size: 'small',
|
|
45252
|
-
type: 'button',
|
|
45253
|
-
state: transitioning() ? 'WORKING' : 'ENABLED',
|
|
45254
|
-
handler: transition.bind(this, 'approved'),
|
|
45255
|
-
}"
|
|
45256
|
-
/>
|
|
45257
|
-
<val-button
|
|
45258
|
-
[props]="{
|
|
45259
|
-
token: 'reject',
|
|
45260
|
-
text: t('reject'),
|
|
45261
|
-
color: 'medium',
|
|
45262
|
-
fill: 'outline',
|
|
45263
|
-
shape: 'round',
|
|
45264
|
-
size: 'small',
|
|
45265
|
-
type: 'button',
|
|
45266
|
-
state: transitioning() ? 'WORKING' : 'ENABLED',
|
|
45267
|
-
handler: transition.bind(this, 'rejected'),
|
|
45268
|
-
}"
|
|
45269
|
-
/>
|
|
45270
|
-
</div>
|
|
45271
|
-
}
|
|
45272
|
-
</div>
|
|
45273
|
-
|
|
45274
|
-
@if (req()!.submitter) {
|
|
45275
|
-
<div class="section">
|
|
45276
|
-
<p class="section__title">{{ t('submitter') }}</p>
|
|
45277
|
-
<div class="submitter-row">
|
|
45278
|
-
<div class="submitter-avatar" aria-hidden="true">{{ submitterInitial() }}</div>
|
|
45279
|
-
<div class="submitter-info">
|
|
45280
|
-
@if (req()!.submitter!.name) {
|
|
45281
|
-
<span class="submitter-name">{{ req()!.submitter!.name }}</span>
|
|
45282
|
-
}
|
|
45283
|
-
@if (req()!.submitter!.email) {
|
|
45284
|
-
<span class="submitter-email">{{ req()!.submitter!.email }}</span>
|
|
45285
|
-
}
|
|
45286
|
-
@if (req()!.submitter!.phone) {
|
|
45287
|
-
<span class="submitter-email">{{ req()!.submitter!.phone }}</span>
|
|
45288
|
-
}
|
|
45289
|
-
</div>
|
|
45290
|
-
</div>
|
|
45291
|
-
</div>
|
|
45292
|
-
}
|
|
45293
|
-
|
|
45294
|
-
@if (fieldEntries().length) {
|
|
45295
|
-
<div class="section">
|
|
45296
|
-
<p class="section__title">{{ t('fields') }}</p>
|
|
45297
|
-
@for (entry of fieldEntries(); track entry.key) {
|
|
45298
|
-
<div class="field-row">
|
|
45299
|
-
<span class="field-row__label">{{ entry.key }}</span>
|
|
45300
|
-
<span class="field-row__value">{{ entry.value }}</span>
|
|
45301
|
-
</div>
|
|
45302
|
-
}
|
|
45303
|
-
</div>
|
|
45304
|
-
}
|
|
45305
|
-
|
|
45306
|
-
@if (
|
|
45307
|
-
!config().canReview() && req()!.status === 'approved' && canNavigateHero() && config().viewEntityCtaLabelKey
|
|
45308
|
-
) {
|
|
45309
|
-
<val-button
|
|
45310
|
-
[props]="{
|
|
45311
|
-
token: 'view-entity',
|
|
45312
|
-
text: t(config().viewEntityCtaLabelKey!),
|
|
45313
|
-
color: 'primary',
|
|
45314
|
-
fill: 'solid',
|
|
45315
|
-
shape: 'round',
|
|
45316
|
-
size: 'default',
|
|
45317
|
-
type: 'button',
|
|
45318
|
-
state: 'ENABLED',
|
|
45319
|
-
handler: onHeroClick.bind(this),
|
|
45320
|
-
}"
|
|
45321
|
-
/>
|
|
45322
|
-
}
|
|
45323
|
-
|
|
45324
|
-
<div class="section">
|
|
45325
|
-
<p class="section__title">{{ t('commentsTitle') }}</p>
|
|
45326
|
-
@if (loadingComments()) {
|
|
45327
|
-
<div class="section-pad">
|
|
45328
|
-
<val-skeleton [props]="{ width: '100%', height: '48px', borderRadius: '8px' }" />
|
|
45329
|
-
</div>
|
|
45330
|
-
} @else if (comments().length === 0) {
|
|
45331
|
-
<p class="no-comments">{{ t('noComments') }}</p>
|
|
45332
|
-
} @else {
|
|
45333
|
-
<ul class="comments">
|
|
45334
|
-
@for (c of comments(); track c.commentId) {
|
|
45335
|
-
<li class="comment">
|
|
45336
|
-
<div class="comment__avatar" aria-hidden="true">{{ authorInitial(c) }}</div>
|
|
45337
|
-
<div class="comment__content">
|
|
45338
|
-
<div class="comment__header">
|
|
45339
|
-
<span class="comment__author">{{ c.author.name || c.author.email }}</span>
|
|
45340
|
-
<span class="comment__date">{{ relativeTime(c.createdAt) }}</span>
|
|
45341
|
-
</div>
|
|
45342
|
-
<p class="comment__body">{{ c.body }}</p>
|
|
45343
|
-
</div>
|
|
45344
|
-
</li>
|
|
45345
|
-
}
|
|
45346
|
-
</ul>
|
|
45347
|
-
}
|
|
45348
|
-
</div>
|
|
45349
|
-
</div>
|
|
45350
|
-
}
|
|
45351
|
-
</div>
|
|
45352
|
-
`, isInline: true, styles: [".page{display:flex;flex-direction:column;gap:14px}.skel{display:flex;flex-direction:column;gap:12px}.skel val-skeleton{display:block}.detail{display:flex;flex-direction:column;gap:14px}.entity-hero{display:flex;align-items:center;gap:14px;padding:14px 16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:14px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));width:100%;text-align:left;cursor:pointer}.entity-hero:disabled{cursor:default}.entity-hero__media{width:56px;height:56px;border-radius:12px;overflow:hidden;flex-shrink:0;background:var(--ion-color-light, #f4f5f8);display:flex;align-items:center;justify-content:center}.entity-hero__img{width:100%;height:100%;object-fit:cover}.entity-hero__placeholder{font-size:1.625rem}.entity-hero__body{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.entity-hero__label{font-size:.75rem;color:var(--ion-color-dark, #636469)}.entity-hero__name{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.entity-hero__chevron{font-size:1.5rem;color:var(--ion-color-dark, #92949c);line-height:1}.status-card{background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-left:4px solid;padding:14px 16px;display:flex;flex-direction:column;gap:12px}.status-card__top{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.status-badge{display:inline-block;padding:4px 12px;border-radius:100px;font-size:.8125rem;font-weight:700}.submitted-at{font-size:.8125rem;color:var(--ion-color-dark-shade, #1e2023)}.actions-row{display:flex;gap:10px;flex-wrap:wrap}.section{background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));overflow:hidden}.section__title{margin:0;font-size:.8125rem;font-weight:600;color:var(--ion-color-dark-shade, #1e2023);padding:12px 16px 0}.section-pad{padding:12px 16px}.submitter-row{display:flex;align-items:center;gap:12px;padding:12px 16px}.submitter-avatar{width:40px;height:40px;border-radius:50%;background:var(--ion-color-primary, #313131);color:var(--ion-color-primary-contrast, #fff);display:flex;align-items:center;justify-content:center;font-size:1rem;font-weight:700;flex-shrink:0}.submitter-info{display:flex;flex-direction:column;gap:2px;min-width:0}.submitter-name{font-size:.9375rem;font-weight:600;color:var(--ion-text-color, #000)}.submitter-email{font-size:.8125rem;color:var(--ion-color-dark, #636469);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.field-row{display:flex;flex-direction:column;gap:3px;padding:11px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .06))}.field-row:last-child{border-bottom:none}.field-row__label{font-size:.75rem;color:var(--ion-color-dark, #636469);text-transform:capitalize}.field-row__value{font-size:.9375rem;color:var(--ion-text-color, #000);font-weight:500;word-break:break-word;white-space:pre-line}.no-comments{margin:0;padding:12px 16px;font-size:.875rem;color:var(--ion-color-dark, #92949c)}.comments{list-style:none;margin:0;padding:0}.comment{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .06))}.comment:last-child{border-bottom:none}.comment__avatar{width:32px;height:32px;border-radius:50%;background:var(--ion-color-primary, #313131);color:var(--ion-color-primary-contrast, #fff);display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;flex-shrink:0}.comment__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.comment__header{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.comment__author{font-size:.875rem;font-weight:600;color:var(--ion-text-color, #000)}.comment__date{font-size:.75rem;color:var(--ion-color-dark, #92949c);white-space:nowrap}.comment__body{margin:0;font-size:.875rem;color:var(--ion-text-color, #000);line-height:1.5;white-space:pre-line}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SkeletonComponent, selector: "val-skeleton", inputs: ["props"] }] }); }
|
|
45353
|
-
}
|
|
45354
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestReviewPanelComponent, decorators: [{
|
|
45355
|
-
type: Component,
|
|
45356
|
-
args: [{ selector: 'val-request-review-panel', standalone: true, imports: [ButtonComponent, EmptyStateComponent, SkeletonComponent], template: `
|
|
45357
|
-
<div class="page">
|
|
45358
|
-
@if (loading()) {
|
|
45359
|
-
<div class="skel" aria-hidden="true">
|
|
45360
|
-
<val-skeleton [props]="{ width: '100%', height: '88px', borderRadius: '14px' }" />
|
|
45361
|
-
<val-skeleton [props]="{ width: '100%', height: '64px', borderRadius: '12px' }" />
|
|
45362
|
-
<val-skeleton [props]="{ width: '100%', height: '80px', borderRadius: '12px' }" />
|
|
45363
|
-
<val-skeleton [props]="{ width: '100%', height: '120px', borderRadius: '12px' }" />
|
|
45364
|
-
</div>
|
|
45365
|
-
} @else if (loadError()) {
|
|
45366
|
-
<val-empty-state [props]="errorState()" />
|
|
45367
|
-
} @else if (req()) {
|
|
45368
|
-
<div class="detail">
|
|
45369
|
-
@if (heroConfig() && heroName()) {
|
|
45370
|
-
<button class="entity-hero" (click)="onHeroClick()" [disabled]="!canNavigateHero()">
|
|
45371
|
-
<div class="entity-hero__media">
|
|
45372
|
-
@if (heroImage() && !heroImgFailed()) {
|
|
45373
|
-
<img
|
|
45374
|
-
[src]="heroImage()"
|
|
45375
|
-
[alt]="heroName()!"
|
|
45376
|
-
loading="lazy"
|
|
45377
|
-
(error)="heroImgFailed.set(true)"
|
|
45378
|
-
class="entity-hero__img"
|
|
45379
|
-
/>
|
|
45380
|
-
} @else {
|
|
45381
|
-
<span class="entity-hero__placeholder" aria-hidden="true">{{
|
|
45382
|
-
heroConfig()!.placeholderIcon || '📄'
|
|
45383
|
-
}}</span>
|
|
45384
|
-
}
|
|
45385
|
-
</div>
|
|
45386
|
-
<div class="entity-hero__body">
|
|
45387
|
-
<span class="entity-hero__label">{{ t(heroConfig()!.labelKey) }}</span>
|
|
45388
|
-
<span class="entity-hero__name">{{ heroName() }}</span>
|
|
45389
|
-
</div>
|
|
45390
|
-
@if (canNavigateHero()) {
|
|
45391
|
-
<span class="entity-hero__chevron" aria-hidden="true">›</span>
|
|
45392
|
-
}
|
|
45393
|
-
</button>
|
|
45394
|
-
}
|
|
45395
|
-
|
|
45396
|
-
<div class="status-card" [style.border-left-color]="statusStyle().bg">
|
|
45397
|
-
<div class="status-card__top">
|
|
45398
|
-
<span class="status-badge" [style.background]="statusStyle().bg" [style.color]="statusStyle().color">
|
|
45399
|
-
{{ t('status_' + req()!.status) }}
|
|
45400
|
-
</span>
|
|
45401
|
-
<span class="submitted-at">{{ t('submittedAt') }}: {{ formatDate(req()!.createdAt) }}</span>
|
|
45402
|
-
</div>
|
|
45403
|
-
|
|
45404
|
-
@if (config().canReview() && (req()!.status === 'pending' || req()!.status === 'in_review')) {
|
|
45405
|
-
<div class="actions-row">
|
|
45406
|
-
<val-button
|
|
45407
|
-
[props]="{
|
|
45408
|
-
token: 'approve',
|
|
45409
|
-
text: t('approve'),
|
|
45410
|
-
color: 'primary',
|
|
45411
|
-
fill: 'solid',
|
|
45412
|
-
shape: 'round',
|
|
45413
|
-
size: 'small',
|
|
45414
|
-
type: 'button',
|
|
45415
|
-
state: transitioning() ? 'WORKING' : 'ENABLED',
|
|
45416
|
-
handler: transition.bind(this, 'approved'),
|
|
45417
|
-
}"
|
|
45418
|
-
/>
|
|
45419
|
-
<val-button
|
|
45420
|
-
[props]="{
|
|
45421
|
-
token: 'reject',
|
|
45422
|
-
text: t('reject'),
|
|
45423
|
-
color: 'medium',
|
|
45424
|
-
fill: 'outline',
|
|
45425
|
-
shape: 'round',
|
|
45426
|
-
size: 'small',
|
|
45427
|
-
type: 'button',
|
|
45428
|
-
state: transitioning() ? 'WORKING' : 'ENABLED',
|
|
45429
|
-
handler: transition.bind(this, 'rejected'),
|
|
45430
|
-
}"
|
|
45431
|
-
/>
|
|
45432
|
-
</div>
|
|
45433
|
-
}
|
|
45434
|
-
</div>
|
|
45435
|
-
|
|
45436
|
-
@if (req()!.submitter) {
|
|
45437
|
-
<div class="section">
|
|
45438
|
-
<p class="section__title">{{ t('submitter') }}</p>
|
|
45439
|
-
<div class="submitter-row">
|
|
45440
|
-
<div class="submitter-avatar" aria-hidden="true">{{ submitterInitial() }}</div>
|
|
45441
|
-
<div class="submitter-info">
|
|
45442
|
-
@if (req()!.submitter!.name) {
|
|
45443
|
-
<span class="submitter-name">{{ req()!.submitter!.name }}</span>
|
|
45444
|
-
}
|
|
45445
|
-
@if (req()!.submitter!.email) {
|
|
45446
|
-
<span class="submitter-email">{{ req()!.submitter!.email }}</span>
|
|
45447
|
-
}
|
|
45448
|
-
@if (req()!.submitter!.phone) {
|
|
45449
|
-
<span class="submitter-email">{{ req()!.submitter!.phone }}</span>
|
|
45450
|
-
}
|
|
45451
|
-
</div>
|
|
45452
|
-
</div>
|
|
45453
|
-
</div>
|
|
45454
|
-
}
|
|
45455
|
-
|
|
45456
|
-
@if (fieldEntries().length) {
|
|
45457
|
-
<div class="section">
|
|
45458
|
-
<p class="section__title">{{ t('fields') }}</p>
|
|
45459
|
-
@for (entry of fieldEntries(); track entry.key) {
|
|
45460
|
-
<div class="field-row">
|
|
45461
|
-
<span class="field-row__label">{{ entry.key }}</span>
|
|
45462
|
-
<span class="field-row__value">{{ entry.value }}</span>
|
|
45463
|
-
</div>
|
|
45464
|
-
}
|
|
45465
|
-
</div>
|
|
45466
|
-
}
|
|
45467
|
-
|
|
45468
|
-
@if (
|
|
45469
|
-
!config().canReview() && req()!.status === 'approved' && canNavigateHero() && config().viewEntityCtaLabelKey
|
|
45470
|
-
) {
|
|
45471
|
-
<val-button
|
|
45472
|
-
[props]="{
|
|
45473
|
-
token: 'view-entity',
|
|
45474
|
-
text: t(config().viewEntityCtaLabelKey!),
|
|
45475
|
-
color: 'primary',
|
|
45476
|
-
fill: 'solid',
|
|
45477
|
-
shape: 'round',
|
|
45478
|
-
size: 'default',
|
|
45479
|
-
type: 'button',
|
|
45480
|
-
state: 'ENABLED',
|
|
45481
|
-
handler: onHeroClick.bind(this),
|
|
45482
|
-
}"
|
|
45483
|
-
/>
|
|
45484
|
-
}
|
|
45485
|
-
|
|
45486
|
-
<div class="section">
|
|
45487
|
-
<p class="section__title">{{ t('commentsTitle') }}</p>
|
|
45488
|
-
@if (loadingComments()) {
|
|
45489
|
-
<div class="section-pad">
|
|
45490
|
-
<val-skeleton [props]="{ width: '100%', height: '48px', borderRadius: '8px' }" />
|
|
45491
|
-
</div>
|
|
45492
|
-
} @else if (comments().length === 0) {
|
|
45493
|
-
<p class="no-comments">{{ t('noComments') }}</p>
|
|
45494
|
-
} @else {
|
|
45495
|
-
<ul class="comments">
|
|
45496
|
-
@for (c of comments(); track c.commentId) {
|
|
45497
|
-
<li class="comment">
|
|
45498
|
-
<div class="comment__avatar" aria-hidden="true">{{ authorInitial(c) }}</div>
|
|
45499
|
-
<div class="comment__content">
|
|
45500
|
-
<div class="comment__header">
|
|
45501
|
-
<span class="comment__author">{{ c.author.name || c.author.email }}</span>
|
|
45502
|
-
<span class="comment__date">{{ relativeTime(c.createdAt) }}</span>
|
|
45503
|
-
</div>
|
|
45504
|
-
<p class="comment__body">{{ c.body }}</p>
|
|
45505
|
-
</div>
|
|
45506
|
-
</li>
|
|
45507
|
-
}
|
|
45508
|
-
</ul>
|
|
45509
|
-
}
|
|
45510
|
-
</div>
|
|
45511
|
-
</div>
|
|
45512
|
-
}
|
|
45513
|
-
</div>
|
|
45514
|
-
`, styles: [".page{display:flex;flex-direction:column;gap:14px}.skel{display:flex;flex-direction:column;gap:12px}.skel val-skeleton{display:block}.detail{display:flex;flex-direction:column;gap:14px}.entity-hero{display:flex;align-items:center;gap:14px;padding:14px 16px;background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:14px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));width:100%;text-align:left;cursor:pointer}.entity-hero:disabled{cursor:default}.entity-hero__media{width:56px;height:56px;border-radius:12px;overflow:hidden;flex-shrink:0;background:var(--ion-color-light, #f4f5f8);display:flex;align-items:center;justify-content:center}.entity-hero__img{width:100%;height:100%;object-fit:cover}.entity-hero__placeholder{font-size:1.625rem}.entity-hero__body{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.entity-hero__label{font-size:.75rem;color:var(--ion-color-dark, #636469)}.entity-hero__name{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.entity-hero__chevron{font-size:1.5rem;color:var(--ion-color-dark, #92949c);line-height:1}.status-card{background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-left:4px solid;padding:14px 16px;display:flex;flex-direction:column;gap:12px}.status-card__top{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.status-badge{display:inline-block;padding:4px 12px;border-radius:100px;font-size:.8125rem;font-weight:700}.submitted-at{font-size:.8125rem;color:var(--ion-color-dark-shade, #1e2023)}.actions-row{display:flex;gap:10px;flex-wrap:wrap}.section{background:var(--ion-card-background, var(--ion-background-color, #fff));border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));overflow:hidden}.section__title{margin:0;font-size:.8125rem;font-weight:600;color:var(--ion-color-dark-shade, #1e2023);padding:12px 16px 0}.section-pad{padding:12px 16px}.submitter-row{display:flex;align-items:center;gap:12px;padding:12px 16px}.submitter-avatar{width:40px;height:40px;border-radius:50%;background:var(--ion-color-primary, #313131);color:var(--ion-color-primary-contrast, #fff);display:flex;align-items:center;justify-content:center;font-size:1rem;font-weight:700;flex-shrink:0}.submitter-info{display:flex;flex-direction:column;gap:2px;min-width:0}.submitter-name{font-size:.9375rem;font-weight:600;color:var(--ion-text-color, #000)}.submitter-email{font-size:.8125rem;color:var(--ion-color-dark, #636469);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.field-row{display:flex;flex-direction:column;gap:3px;padding:11px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .06))}.field-row:last-child{border-bottom:none}.field-row__label{font-size:.75rem;color:var(--ion-color-dark, #636469);text-transform:capitalize}.field-row__value{font-size:.9375rem;color:var(--ion-text-color, #000);font-weight:500;word-break:break-word;white-space:pre-line}.no-comments{margin:0;padding:12px 16px;font-size:.875rem;color:var(--ion-color-dark, #92949c)}.comments{list-style:none;margin:0;padding:0}.comment{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .06))}.comment:last-child{border-bottom:none}.comment__avatar{width:32px;height:32px;border-radius:50%;background:var(--ion-color-primary, #313131);color:var(--ion-color-primary-contrast, #fff);display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;flex-shrink:0}.comment__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.comment__header{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.comment__author{font-size:.875rem;font-weight:600;color:var(--ion-text-color, #000)}.comment__date{font-size:.75rem;color:var(--ion-color-dark, #92949c);white-space:nowrap}.comment__body{margin:0;font-size:.875rem;color:var(--ion-text-color, #000);line-height:1.5;white-space:pre-line}\n"] }]
|
|
45515
|
-
}], ctorParameters: () => [] });
|
|
45516
|
-
|
|
45517
45686
|
/**
|
|
45518
45687
|
* `val-cookie-banner` — bottom/top fixed banner asking the user to choose
|
|
45519
45688
|
* a cookie consent option. Presentational only: emits events on each
|
|
@@ -54835,260 +55004,6 @@ function buildSideNavItemsFromBottomNav(config, options = {}) {
|
|
|
54835
55004
|
}));
|
|
54836
55005
|
}
|
|
54837
55006
|
|
|
54838
|
-
/**
|
|
54839
|
-
* Default values for image processing
|
|
54840
|
-
*/
|
|
54841
|
-
const IMAGE_DEFAULTS = {
|
|
54842
|
-
maxWidth: 800,
|
|
54843
|
-
maxHeight: 800,
|
|
54844
|
-
quality: 0.8,
|
|
54845
|
-
mimeType: 'image/jpeg',
|
|
54846
|
-
maxSize: 10 * 1024 * 1024, // 10MB
|
|
54847
|
-
allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
|
|
54848
|
-
thumbnailSize: 150,
|
|
54849
|
-
};
|
|
54850
|
-
|
|
54851
|
-
/**
|
|
54852
|
-
* ImageService
|
|
54853
|
-
*
|
|
54854
|
-
* Service for image processing including compression, thumbnails, cropping and validation.
|
|
54855
|
-
* Uses HTML Canvas for all operations - no external dependencies.
|
|
54856
|
-
*
|
|
54857
|
-
* @example
|
|
54858
|
-
* ```typescript
|
|
54859
|
-
* const imageService = inject(ImageService);
|
|
54860
|
-
*
|
|
54861
|
-
* // Compress an image
|
|
54862
|
-
* const compressed = await imageService.compress(file, { maxWidth: 800, quality: 0.8 });
|
|
54863
|
-
*
|
|
54864
|
-
* // Generate thumbnail
|
|
54865
|
-
* const thumb = await imageService.thumbnail(file, 150);
|
|
54866
|
-
*
|
|
54867
|
-
* // Validate before processing
|
|
54868
|
-
* const validation = imageService.validate(file, { maxSize: 5 * 1024 * 1024 });
|
|
54869
|
-
* if (!validation.valid) {
|
|
54870
|
-
* console.error(validation.message);
|
|
54871
|
-
* }
|
|
54872
|
-
* ```
|
|
54873
|
-
*/
|
|
54874
|
-
class ImageService {
|
|
54875
|
-
/**
|
|
54876
|
-
* Compress an image maintaining aspect ratio
|
|
54877
|
-
* @param file - File or Blob to compress
|
|
54878
|
-
* @param options - Compression options
|
|
54879
|
-
* @returns Promise with processed image data
|
|
54880
|
-
*/
|
|
54881
|
-
async compress(file, options) {
|
|
54882
|
-
const opts = {
|
|
54883
|
-
maxWidth: options?.maxWidth ?? IMAGE_DEFAULTS.maxWidth,
|
|
54884
|
-
maxHeight: options?.maxHeight ?? IMAGE_DEFAULTS.maxHeight,
|
|
54885
|
-
quality: options?.quality ?? IMAGE_DEFAULTS.quality,
|
|
54886
|
-
mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
|
|
54887
|
-
};
|
|
54888
|
-
const img = await this.loadImage(file);
|
|
54889
|
-
const { width, height } = this.calculateDimensions(img.width, img.height, opts.maxWidth, opts.maxHeight);
|
|
54890
|
-
const canvas = document.createElement('canvas');
|
|
54891
|
-
canvas.width = width;
|
|
54892
|
-
canvas.height = height;
|
|
54893
|
-
const ctx = canvas.getContext('2d');
|
|
54894
|
-
ctx.drawImage(img, 0, 0, width, height);
|
|
54895
|
-
const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
|
|
54896
|
-
const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
|
|
54897
|
-
return {
|
|
54898
|
-
blob,
|
|
54899
|
-
dataUrl,
|
|
54900
|
-
width,
|
|
54901
|
-
height,
|
|
54902
|
-
size: blob.size,
|
|
54903
|
-
};
|
|
54904
|
-
}
|
|
54905
|
-
/**
|
|
54906
|
-
* Generate a square thumbnail from an image
|
|
54907
|
-
* @param file - File or Blob to process
|
|
54908
|
-
* @param size - Thumbnail size in pixels (default: 150)
|
|
54909
|
-
* @returns Promise with processed thumbnail
|
|
54910
|
-
*/
|
|
54911
|
-
async thumbnail(file, size) {
|
|
54912
|
-
const thumbSize = size ?? IMAGE_DEFAULTS.thumbnailSize;
|
|
54913
|
-
const img = await this.loadImage(file);
|
|
54914
|
-
// Calculate square crop from center
|
|
54915
|
-
const minDim = Math.min(img.width, img.height);
|
|
54916
|
-
const cropX = (img.width - minDim) / 2;
|
|
54917
|
-
const cropY = (img.height - minDim) / 2;
|
|
54918
|
-
const canvas = document.createElement('canvas');
|
|
54919
|
-
canvas.width = thumbSize;
|
|
54920
|
-
canvas.height = thumbSize;
|
|
54921
|
-
const ctx = canvas.getContext('2d');
|
|
54922
|
-
ctx.drawImage(img, cropX, cropY, minDim, minDim, 0, 0, thumbSize, thumbSize);
|
|
54923
|
-
const blob = await this.canvasToBlob(canvas, IMAGE_DEFAULTS.mimeType, 0.7 // Lower quality for thumbnails
|
|
54924
|
-
);
|
|
54925
|
-
const dataUrl = canvas.toDataURL(IMAGE_DEFAULTS.mimeType, 0.7);
|
|
54926
|
-
return {
|
|
54927
|
-
blob,
|
|
54928
|
-
dataUrl,
|
|
54929
|
-
width: thumbSize,
|
|
54930
|
-
height: thumbSize,
|
|
54931
|
-
size: blob.size,
|
|
54932
|
-
};
|
|
54933
|
-
}
|
|
54934
|
-
/**
|
|
54935
|
-
* Crop an image with specific coordinates
|
|
54936
|
-
* @param file - File or Blob to crop
|
|
54937
|
-
* @param cropData - Crop coordinates and dimensions
|
|
54938
|
-
* @param options - Optional compression options for output
|
|
54939
|
-
* @returns Promise with cropped image
|
|
54940
|
-
*/
|
|
54941
|
-
async crop(file, cropData, options) {
|
|
54942
|
-
const img = await this.loadImage(file);
|
|
54943
|
-
const opts = {
|
|
54944
|
-
quality: options?.quality ?? IMAGE_DEFAULTS.quality,
|
|
54945
|
-
mimeType: options?.mimeType ?? IMAGE_DEFAULTS.mimeType,
|
|
54946
|
-
};
|
|
54947
|
-
const canvas = document.createElement('canvas');
|
|
54948
|
-
canvas.width = cropData.width;
|
|
54949
|
-
canvas.height = cropData.height;
|
|
54950
|
-
const ctx = canvas.getContext('2d');
|
|
54951
|
-
ctx.drawImage(img, cropData.x, cropData.y, cropData.width, cropData.height, 0, 0, cropData.width, cropData.height);
|
|
54952
|
-
// Apply max dimensions if specified
|
|
54953
|
-
if (options?.maxWidth || options?.maxHeight) {
|
|
54954
|
-
return this.compress(await this.canvasToBlob(canvas, opts.mimeType, 1), options);
|
|
54955
|
-
}
|
|
54956
|
-
const blob = await this.canvasToBlob(canvas, opts.mimeType, opts.quality);
|
|
54957
|
-
const dataUrl = canvas.toDataURL(opts.mimeType, opts.quality);
|
|
54958
|
-
return {
|
|
54959
|
-
blob,
|
|
54960
|
-
dataUrl,
|
|
54961
|
-
width: cropData.width,
|
|
54962
|
-
height: cropData.height,
|
|
54963
|
-
size: blob.size,
|
|
54964
|
-
};
|
|
54965
|
-
}
|
|
54966
|
-
/**
|
|
54967
|
-
* Validate an image file before processing
|
|
54968
|
-
* @param file - File to validate
|
|
54969
|
-
* @param options - Validation options
|
|
54970
|
-
* @returns Validation result with error details if invalid
|
|
54971
|
-
*/
|
|
54972
|
-
validate(file, options) {
|
|
54973
|
-
const opts = {
|
|
54974
|
-
maxSize: options?.maxSize ?? IMAGE_DEFAULTS.maxSize,
|
|
54975
|
-
allowedTypes: options?.allowedTypes ?? IMAGE_DEFAULTS.allowedTypes,
|
|
54976
|
-
};
|
|
54977
|
-
// Check file type
|
|
54978
|
-
if (!opts.allowedTypes.includes(file.type)) {
|
|
54979
|
-
return {
|
|
54980
|
-
valid: false,
|
|
54981
|
-
error: 'invalidType',
|
|
54982
|
-
message: `Formato no válido. Usa: ${opts.allowedTypes.map(t => t.split('/')[1].toUpperCase()).join(', ')}`,
|
|
54983
|
-
};
|
|
54984
|
-
}
|
|
54985
|
-
// Check file size
|
|
54986
|
-
if (file.size > opts.maxSize) {
|
|
54987
|
-
const maxMB = Math.round(opts.maxSize / (1024 * 1024));
|
|
54988
|
-
return {
|
|
54989
|
-
valid: false,
|
|
54990
|
-
error: 'fileTooLarge',
|
|
54991
|
-
message: `La imagen es muy grande. Máximo ${maxMB}MB`,
|
|
54992
|
-
};
|
|
54993
|
-
}
|
|
54994
|
-
return { valid: true };
|
|
54995
|
-
}
|
|
54996
|
-
/**
|
|
54997
|
-
* Validate image dimensions (async - requires loading image)
|
|
54998
|
-
* @param file - File to validate
|
|
54999
|
-
* @param options - Validation options with minWidth/minHeight
|
|
55000
|
-
* @returns Promise with validation result
|
|
55001
|
-
*/
|
|
55002
|
-
async validateDimensions(file, options) {
|
|
55003
|
-
const img = await this.loadImage(file);
|
|
55004
|
-
if (options.minWidth && img.width < options.minWidth) {
|
|
55005
|
-
return {
|
|
55006
|
-
valid: false,
|
|
55007
|
-
error: 'imageTooSmall',
|
|
55008
|
-
message: `La imagen debe tener al menos ${options.minWidth}px de ancho`,
|
|
55009
|
-
};
|
|
55010
|
-
}
|
|
55011
|
-
if (options.minHeight && img.height < options.minHeight) {
|
|
55012
|
-
return {
|
|
55013
|
-
valid: false,
|
|
55014
|
-
error: 'imageTooSmall',
|
|
55015
|
-
message: `La imagen debe tener al menos ${options.minHeight}px de alto`,
|
|
55016
|
-
};
|
|
55017
|
-
}
|
|
55018
|
-
return { valid: true };
|
|
55019
|
-
}
|
|
55020
|
-
/**
|
|
55021
|
-
* Convert a Blob/File to a data URL
|
|
55022
|
-
*/
|
|
55023
|
-
async toDataUrl(file) {
|
|
55024
|
-
return new Promise((resolve, reject) => {
|
|
55025
|
-
const reader = new FileReader();
|
|
55026
|
-
reader.onload = () => resolve(reader.result);
|
|
55027
|
-
reader.onerror = reject;
|
|
55028
|
-
reader.readAsDataURL(file);
|
|
55029
|
-
});
|
|
55030
|
-
}
|
|
55031
|
-
/**
|
|
55032
|
-
* Convert a data URL to a Blob
|
|
55033
|
-
*/
|
|
55034
|
-
dataUrlToBlob(dataUrl) {
|
|
55035
|
-
const arr = dataUrl.split(',');
|
|
55036
|
-
const mime = arr[0].match(/:(.*?);/)[1];
|
|
55037
|
-
const bstr = atob(arr[1]);
|
|
55038
|
-
let n = bstr.length;
|
|
55039
|
-
const u8arr = new Uint8Array(n);
|
|
55040
|
-
while (n--) {
|
|
55041
|
-
u8arr[n] = bstr.charCodeAt(n);
|
|
55042
|
-
}
|
|
55043
|
-
return new Blob([u8arr], { type: mime });
|
|
55044
|
-
}
|
|
55045
|
-
// ============== Private Helpers ==============
|
|
55046
|
-
loadImage(file) {
|
|
55047
|
-
return new Promise((resolve, reject) => {
|
|
55048
|
-
const img = new Image();
|
|
55049
|
-
img.onload = () => {
|
|
55050
|
-
URL.revokeObjectURL(img.src);
|
|
55051
|
-
resolve(img);
|
|
55052
|
-
};
|
|
55053
|
-
img.onerror = reject;
|
|
55054
|
-
img.src = URL.createObjectURL(file);
|
|
55055
|
-
});
|
|
55056
|
-
}
|
|
55057
|
-
calculateDimensions(originalWidth, originalHeight, maxWidth, maxHeight) {
|
|
55058
|
-
let width = originalWidth;
|
|
55059
|
-
let height = originalHeight;
|
|
55060
|
-
// Scale down if necessary, maintaining aspect ratio
|
|
55061
|
-
if (width > maxWidth) {
|
|
55062
|
-
height = (height * maxWidth) / width;
|
|
55063
|
-
width = maxWidth;
|
|
55064
|
-
}
|
|
55065
|
-
if (height > maxHeight) {
|
|
55066
|
-
width = (width * maxHeight) / height;
|
|
55067
|
-
height = maxHeight;
|
|
55068
|
-
}
|
|
55069
|
-
return {
|
|
55070
|
-
width: Math.round(width),
|
|
55071
|
-
height: Math.round(height),
|
|
55072
|
-
};
|
|
55073
|
-
}
|
|
55074
|
-
canvasToBlob(canvas, mimeType, quality) {
|
|
55075
|
-
return new Promise((resolve, reject) => {
|
|
55076
|
-
canvas.toBlob((blob) => {
|
|
55077
|
-
if (blob)
|
|
55078
|
-
resolve(blob);
|
|
55079
|
-
else
|
|
55080
|
-
reject(new Error('Failed to create blob from canvas'));
|
|
55081
|
-
}, mimeType, quality);
|
|
55082
|
-
});
|
|
55083
|
-
}
|
|
55084
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
55085
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, providedIn: 'root' }); }
|
|
55086
|
-
}
|
|
55087
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ImageService, decorators: [{
|
|
55088
|
-
type: Injectable,
|
|
55089
|
-
args: [{ providedIn: 'root' }]
|
|
55090
|
-
}] });
|
|
55091
|
-
|
|
55092
55007
|
/**
|
|
55093
55008
|
* Default values
|
|
55094
55009
|
*/
|
|
@@ -83441,6 +83356,100 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
83441
83356
|
* `markdown-article-parser` existente.
|
|
83442
83357
|
*/
|
|
83443
83358
|
|
|
83359
|
+
/**
|
|
83360
|
+
* Contratos del onboarding de pasarelas de pago (ADR-028).
|
|
83361
|
+
*
|
|
83362
|
+
* Espejo reducido del backend: solo lo que una app necesita para conectar la
|
|
83363
|
+
* cuenta de una organización y saber si puede cobrar.
|
|
83364
|
+
*/
|
|
83365
|
+
|
|
83366
|
+
/** Token de inyección de la configuración de pagos. */
|
|
83367
|
+
const VALTECH_PAYMENTS_CONFIG = new InjectionToken('ValtechPaymentsConfig');
|
|
83368
|
+
const DEFAULT_PAYMENTS_CONFIG = {
|
|
83369
|
+
paymentsPrefix: '/v1/payments',
|
|
83370
|
+
};
|
|
83371
|
+
/**
|
|
83372
|
+
* Provee el cliente de pasarelas de pago.
|
|
83373
|
+
*
|
|
83374
|
+
* @example
|
|
83375
|
+
* ```typescript
|
|
83376
|
+
* providePaymentsGateway({ apiUrl: environment.apiUrl }),
|
|
83377
|
+
* ```
|
|
83378
|
+
*/
|
|
83379
|
+
function providePaymentsGateway(config) {
|
|
83380
|
+
return makeEnvironmentProviders([
|
|
83381
|
+
{
|
|
83382
|
+
provide: VALTECH_PAYMENTS_CONFIG,
|
|
83383
|
+
useValue: { ...DEFAULT_PAYMENTS_CONFIG, ...config },
|
|
83384
|
+
},
|
|
83385
|
+
]);
|
|
83386
|
+
}
|
|
83387
|
+
|
|
83388
|
+
/**
|
|
83389
|
+
* Cliente del onboarding de pasarelas de pago (ADR-028).
|
|
83390
|
+
*
|
|
83391
|
+
* Conecta la cuenta de UNA ORGANIZACIÓN con una pasarela, para que el dinero
|
|
83392
|
+
* le llegue directo. Ese es el punto: si el dinero pasara por una cuenta de
|
|
83393
|
+
* Valtech, Valtech sería un PSP regulado por la CMF (ver ADR-028). Por eso
|
|
83394
|
+
* todas las operaciones son por `orgId`, y no existe acá un modo "cuenta de la
|
|
83395
|
+
* plataforma".
|
|
83396
|
+
*
|
|
83397
|
+
* Vive en la lib y no en una app porque el flujo es idéntico en todas: Bingo
|
|
83398
|
+
* lo usa para que cada colegio cobre sus cartones, y Chesed para que cada
|
|
83399
|
+
* refugio reciba aportes. Lo que cambia entre apps es qué se cobra, no cómo se
|
|
83400
|
+
* conecta la cuenta.
|
|
83401
|
+
*
|
|
83402
|
+
* Autenticación: no agrega el Bearer a mano — lo pone el interceptor de
|
|
83403
|
+
* `provideValtechAuth`. Errores: no se capturan acá; los maneja la pantalla.
|
|
83404
|
+
*/
|
|
83405
|
+
class PaymentsGatewayService {
|
|
83406
|
+
constructor() {
|
|
83407
|
+
this.http = inject(HttpClient);
|
|
83408
|
+
this.config = inject(VALTECH_PAYMENTS_CONFIG, { optional: true });
|
|
83409
|
+
}
|
|
83410
|
+
get base() {
|
|
83411
|
+
const apiUrl = this.config?.apiUrl ?? '';
|
|
83412
|
+
const prefix = this.config?.paymentsPrefix ?? '/v1/payments';
|
|
83413
|
+
return `${apiUrl}${prefix}`;
|
|
83414
|
+
}
|
|
83415
|
+
/**
|
|
83416
|
+
* Estado de conexión de la organización con una pasarela.
|
|
83417
|
+
*
|
|
83418
|
+
* Es lo que decide si la app puede ofrecer el cobro: `connected` habilita,
|
|
83419
|
+
* `expired` y `revoked` NO. Mostrar un botón de pago con un token revocado
|
|
83420
|
+
* lleva al usuario a un cobro que falla después de que ya decidió pagar.
|
|
83421
|
+
*/
|
|
83422
|
+
getConnectionStatus(orgId, gateway) {
|
|
83423
|
+
return this.http.get(`${this.base}/credentials/orgs/${orgId}/${gateway}/connection-status`);
|
|
83424
|
+
}
|
|
83425
|
+
/**
|
|
83426
|
+
* URL de autorización OAuth, con su `state` CSRF.
|
|
83427
|
+
*
|
|
83428
|
+
* El caller guarda el `state` antes de redirigir y lo compara al volver: sin
|
|
83429
|
+
* eso, cualquiera podría completar la conexión de otra organización.
|
|
83430
|
+
*/
|
|
83431
|
+
getOAuthAuthorizeURL(orgId, gateway) {
|
|
83432
|
+
return this.http.get(`${this.base}/credentials/orgs/${orgId}/${gateway}/oauth/authorize-url`);
|
|
83433
|
+
}
|
|
83434
|
+
/** Desconecta la credencial de la organización para esa pasarela. */
|
|
83435
|
+
disconnect(orgId, gateway) {
|
|
83436
|
+
return this.http.delete(`${this.base}/credentials/orgs/${orgId}/${gateway}`);
|
|
83437
|
+
}
|
|
83438
|
+
/**
|
|
83439
|
+
* Guarda una credencial de acceso directo (pasarelas sin OAuth, ej. Khipu).
|
|
83440
|
+
* El token es el secreto del cobrador.
|
|
83441
|
+
*/
|
|
83442
|
+
setCredential(orgId, gateway, accessToken) {
|
|
83443
|
+
return this.http.put(`${this.base}/credentials/orgs/${orgId}/${gateway}`, { accessToken });
|
|
83444
|
+
}
|
|
83445
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaymentsGatewayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
83446
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaymentsGatewayService, providedIn: 'root' }); }
|
|
83447
|
+
}
|
|
83448
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaymentsGatewayService, decorators: [{
|
|
83449
|
+
type: Injectable,
|
|
83450
|
+
args: [{ providedIn: 'root' }]
|
|
83451
|
+
}] });
|
|
83452
|
+
|
|
83444
83453
|
const VALTECH_WHATSAPP_CONFIG = new InjectionToken('ValtechWhatsappConfig');
|
|
83445
83454
|
function provideValtechWhatsapp(config) {
|
|
83446
83455
|
return makeEnvironmentProviders([{ provide: VALTECH_WHATSAPP_CONFIG, useValue: config }]);
|
|
@@ -92953,5 +92962,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
92953
92962
|
* Generated bundle index. Do not edit.
|
|
92954
92963
|
*/
|
|
92955
92964
|
|
|
92956
|
-
export { ACCESS_REQUEST_TYPE, ACCESS_REQUEST_VIEW_I18N, ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccessControlService, AccessRequestViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimalCardComponent, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BackButtonComponent, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, BubbleBlitzGameComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CanAccessDirective, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, ChatComposerComponent, ChatInputComponent, ChatWindowComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ChipSelectComponent, ClientTelemetryService, CodeDisplayComponent, CodeValidatorComponent, CollectionsService, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CommsPreferencesService, CommsPreferencesSettingsComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ConfirmationDialogV2Component, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentReactionModalComponent, ContentService, ContentTransformer, ConversationListItemComponent, ConversationService, CookieBannerComponent, CookieSettingsComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CANONICAL_FIELD_ALIASES, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMOJI_RATING_FACES, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LOGIN_LOGO, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_POST_UPDATE_GRACE_MS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DangerSectionComponent, DataTableComponent, DatasetPaginationService, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EXPECTED_NOT_FOUND, EditOrgModalComponent, EmojiRatingComponent, EmptyStateComponent, EntityCardComponent, EntityFeedService, EntradaSerializada, ExpandableTextComponent, ExternalBrowserService, FEATURES_LIST_DEFAULTS, FIELD_TYPES_WITH_OPTIONS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeatureControlService, FeatureGuardDirective, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FieldOptionsEditorComponent, FieldSchemaEditorComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FolderTabsComponent, FontSizeOption, FontSizeSelectorComponent, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormFieldComponent, FormSchemaBuilderService, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GAME_AVATAR_CATALOG_SIZE_PER_STYLE, GameAvatarComponent, GameProfileService, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, GroupMembersComponent, GroupPickerComponent, GroupsService, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandleService, HandoffService, HapticsService, HasPermissionDirective, HeaderActionsService, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, HtmlViewerModalComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, KNOWN_ROUTES, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGGED_IN_HINT_COOKIE, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LanguageSelectorV2Component, LayeredCardComponent, LegalContentService, LegalLinkService, LightRippleDirective, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LiveReadFallbackService, LoadMoreComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METADATA_LIST_DEFAULTS, META_SCHEMA_VERSION, METRIC_CARD_DEFAULTS, MINI_GAMES_I18N, MINI_GAME_PLAYER_AVATARS, MINI_GAME_PLAYER_COLORS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MediaViewerModalComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MemoryGameComponent, MenuComponent, MessageBubbleComponent, MessagingService, MetaService, MetadataListComponent, MeteringService, MetricCardComponent, MfaModalComponent, MiniGameCalloutComponent, MiniGamePlayerProfileService, MiniGameScorePopComponent, MiniGamesMenuComponent, ModalLayoutComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NoticeComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberPickerComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OperationReferenceComponent, OptionCardsComponent, OptionSheetComponent, OptionSheetService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PERSONA_CONFIG, PLATFORM_CONFIGS, POST_UPDATE_TS_KEY, PageBlockComponent, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PdfService, PermissionCatalogService, PermissionSelectorComponent, PermissionsModalComponent, PermissionsViewComponent, PersonaService, PhoneDisplayComponent, PhoneFormatService, PhoneInputComponent, PickerV2Component, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PricingTableComponent, ProcessLinksPipe, ProfileCardComponent, ProfileContentComponent, ProfileModalComponent, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrBrandValidationError, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RbacService, ReactionBarComponent, ReactionsService, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestFormComponent, RequestModalComponent, RequestReviewPanelComponent, RequestService, RetroAudioService, RichEditorComponent, RightsFooterComponent, RoleManagerComponent, 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, SURVEY_QUESTION_TYPES, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SectionHeaderComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SelectSearchPickerModalComponent, SessionListModalComponent, SessionService, SessionTransitionOverlayComponent, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimonGameComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashComponent, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SupportTicketCtaComponent, SurveyBuilderComponent, SurveyResponseComponent, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeSelectorComponent, ThemeService, ThreadPanelComponent, TicketCardComponent, TicketCardImageService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, TypingIndicatorComponent, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UsageMetersComponent, UsageService, UserAvatarComponent, UsernameInputComponent, VALTECH_ACCESS_FEATURES, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_CHAT_CONFIG, VALTECH_COLLECTIONS_CONFIG, VALTECH_COMMS_PREFERENCES_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_COPYRIGHT_TEMPLATE, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DIAGRAMS, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_LEGAL_ENTITY, VALTECH_MENU_I18N, VALTECH_NETWORK_ERROR_KEY, VALTECH_REACTIONS_CONFIG, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VALTECH_WHATSAPP_CONFIG, VAL_REGISTERED_ICONS, VERSION, ValCommentThreadComponent, ValQuotaWarningComponent, ValtechErrorService, VerifyViewComponent, VideoPlayerComponent, VideoUploadService, WhatsappFabComponent, WhatsappService, WizardComponent, WizardFooterComponent, WorkflowService, accessGuard, accessGuardFromRoute, applyDefaultValueToControl, articleToTiptapDoc, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildPlatformMenu, buildSettingsCards, buildSideNavItemsFromBottomNav, button, canSubmitRequestType, classifyChip, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGameAvatarCatalog, createGameAvatarProps, createGlowCardProps, createInitialDatasetState, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, datasetPageFromLegacyCursor, defaultQrBrand, docs, errorLoggingInterceptor, evaluateValtechAccess, extractPathParams, firmaDeSesionDeCustomToken, formatClockTime, formatDateSeparator, formatRelativeTime, gameAvatarCatalogEntryToMetadata, gameAvatarDataUri, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, groupPermissionsByScope, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isKnownRoute, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, providePersona, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccessFeatures, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechChat, provideValtechCollections, provideValtechCommsPreferences, provideValtechContent, provideValtechDebugConsole, provideValtechDiagrams, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationClickActions, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechReactions, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, provideValtechWhatsapp, qrContrastRatio, qrErrorCorrectionFor, query, rbacGuard, renderGameAvatarSvg, renderPatternSvgInner, replaceSpecialChars, requestSubmitMode, resolveColor, resolveCopyrightTemplate, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, roleOf, selectableRequestTypes, storagePaths, suggestEmailFix, superAdminGuard, supportedGameAvatarStyles, tiptapDocToArticle, tiptapDocToArticleElements, toArticle, validateQrBrand, validateRoutes };
|
|
92965
|
+
export { ACCESS_REQUEST_TYPE, ACCESS_REQUEST_VIEW_I18N, ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccessControlService, AccessRequestViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimalCardComponent, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BackButtonComponent, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, BubbleBlitzGameComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CanAccessDirective, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, ChatComposerComponent, ChatInputComponent, ChatWindowComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ChipSelectComponent, ClientTelemetryService, CodeDisplayComponent, CodeValidatorComponent, CollectionsService, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CommsPreferencesService, CommsPreferencesSettingsComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ConfirmationDialogV2Component, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentReactionModalComponent, ContentService, ContentTransformer, ConversationListItemComponent, ConversationService, CookieBannerComponent, CookieSettingsComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CANONICAL_FIELD_ALIASES, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMOJI_RATING_FACES, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LOGIN_LOGO, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENTS_CONFIG, DEFAULT_PLATFORMS, DEFAULT_POST_UPDATE_GRACE_MS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DangerSectionComponent, DataTableComponent, DatasetPaginationService, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EXPECTED_NOT_FOUND, EditOrgModalComponent, EmojiRatingComponent, EmptyStateComponent, EntityCardComponent, EntityFeedService, EntradaSerializada, ExpandableTextComponent, ExternalBrowserService, FEATURES_LIST_DEFAULTS, FIELD_TYPES_WITH_OPTIONS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeatureControlService, FeatureGuardDirective, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FieldOptionsEditorComponent, FieldSchemaEditorComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FolderTabsComponent, FontSizeOption, FontSizeSelectorComponent, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormFieldComponent, FormSchemaBuilderService, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GAME_AVATAR_CATALOG_SIZE_PER_STYLE, GameAvatarComponent, GameProfileService, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, GroupMembersComponent, GroupPickerComponent, GroupsService, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandleService, HandoffService, HapticsService, HasPermissionDirective, HeaderActionsService, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, HtmlViewerModalComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, KNOWN_ROUTES, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGGED_IN_HINT_COOKIE, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LanguageSelectorV2Component, LayeredCardComponent, LegalContentService, LegalLinkService, LightRippleDirective, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LiveReadFallbackService, LoadMoreComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METADATA_LIST_DEFAULTS, META_SCHEMA_VERSION, METRIC_CARD_DEFAULTS, MINI_GAMES_I18N, MINI_GAME_PLAYER_AVATARS, MINI_GAME_PLAYER_COLORS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MediaViewerModalComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MemoryGameComponent, MenuComponent, MessageBubbleComponent, MessagingService, MetaService, MetadataListComponent, MeteringService, MetricCardComponent, MfaModalComponent, MiniGameCalloutComponent, MiniGamePlayerProfileService, MiniGameScorePopComponent, MiniGamesMenuComponent, ModalLayoutComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NoticeComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberPickerComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OperationReferenceComponent, OptionCardsComponent, OptionSheetComponent, OptionSheetService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PERSONA_CONFIG, PLATFORM_CONFIGS, POST_UPDATE_TS_KEY, PageBlockComponent, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PaymentsGatewayService, PdfService, PermissionCatalogService, PermissionSelectorComponent, PermissionsModalComponent, PermissionsViewComponent, PersonaService, PhoneDisplayComponent, PhoneFormatService, PhoneInputComponent, PickerV2Component, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PricingTableComponent, ProcessLinksPipe, ProfileCardComponent, ProfileContentComponent, ProfileModalComponent, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrBrandValidationError, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RbacService, ReactionBarComponent, ReactionsService, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestFormComponent, RequestModalComponent, RequestReviewPanelComponent, RequestService, RetroAudioService, RichEditorComponent, RightsFooterComponent, RoleManagerComponent, 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, SURVEY_QUESTION_TYPES, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SectionHeaderComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SelectSearchPickerModalComponent, SessionListModalComponent, SessionService, SessionTransitionOverlayComponent, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimonGameComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashComponent, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SupportTicketCtaComponent, SurveyBuilderComponent, SurveyResponseComponent, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeSelectorComponent, ThemeService, ThreadPanelComponent, TicketCardComponent, TicketCardImageService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, TypingIndicatorComponent, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UsageMetersComponent, UsageService, UserAvatarComponent, UsernameInputComponent, VALTECH_ACCESS_FEATURES, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_CHAT_CONFIG, VALTECH_COLLECTIONS_CONFIG, VALTECH_COMMS_PREFERENCES_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_COPYRIGHT_TEMPLATE, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DIAGRAMS, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_LEGAL_ENTITY, VALTECH_MENU_I18N, VALTECH_NETWORK_ERROR_KEY, VALTECH_PAYMENTS_CONFIG, VALTECH_REACTIONS_CONFIG, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VALTECH_WHATSAPP_CONFIG, VAL_REGISTERED_ICONS, VERSION, ValCommentThreadComponent, ValQuotaWarningComponent, ValtechErrorService, VerifyViewComponent, VideoPlayerComponent, VideoUploadService, WhatsappFabComponent, WhatsappService, WizardComponent, WizardFooterComponent, WorkflowService, accessGuard, accessGuardFromRoute, applyDefaultValueToControl, articleToTiptapDoc, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildPlatformMenu, buildSettingsCards, buildSideNavItemsFromBottomNav, button, canSubmitRequestType, classifyChip, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGameAvatarCatalog, createGameAvatarProps, createGlowCardProps, createInitialDatasetState, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, datasetPageFromLegacyCursor, defaultQrBrand, docs, errorLoggingInterceptor, evaluateValtechAccess, extractPathParams, firmaDeSesionDeCustomToken, formatClockTime, formatDateSeparator, formatRelativeTime, gameAvatarCatalogEntryToMetadata, gameAvatarDataUri, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, groupPermissionsByScope, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isKnownRoute, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, providePaymentsGateway, providePersona, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccessFeatures, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechChat, provideValtechCollections, provideValtechCommsPreferences, provideValtechContent, provideValtechDebugConsole, provideValtechDiagrams, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationClickActions, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechReactions, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, provideValtechWhatsapp, qrContrastRatio, qrErrorCorrectionFor, query, rbacGuard, renderGameAvatarSvg, renderPatternSvgInner, replaceSpecialChars, requestSubmitMode, resolveColor, resolveCopyrightTemplate, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, roleOf, selectableRequestTypes, storagePaths, suggestEmailFix, superAdminGuard, supportedGameAvatarStyles, tiptapDocToArticle, tiptapDocToArticleElements, toArticle, validateQrBrand, validateRoutes };
|
|
92957
92966
|
//# sourceMappingURL=valtech-components.mjs.map
|