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
|
@@ -9,6 +9,7 @@ export declare class AttachmentUploaderComponent implements OnInit {
|
|
|
9
9
|
attachmentsChange: import("@angular/core").OutputEmitterRef<AttachmentItem[]>;
|
|
10
10
|
protected i18n: I18nService;
|
|
11
11
|
private feedbackService;
|
|
12
|
+
private imageService;
|
|
12
13
|
attachments: import("@angular/core").WritableSignal<AttachmentItem[]>;
|
|
13
14
|
showCameraOverlay: import("@angular/core").WritableSignal<boolean>;
|
|
14
15
|
private cameraStream;
|
|
@@ -26,6 +27,19 @@ export declare class AttachmentUploaderComponent implements OnInit {
|
|
|
26
27
|
onFilesSelected(event: Event): Promise<void>;
|
|
27
28
|
private processFiles;
|
|
28
29
|
private compressImage;
|
|
30
|
+
/**
|
|
31
|
+
* Genera y sube la miniatura de una imagen ya comprimida.
|
|
32
|
+
*
|
|
33
|
+
* Recibe el archivo COMPRIMIDO a propósito: `compressorjs` ya normalizó la
|
|
34
|
+
* orientación EXIF, y `ImageService` trabaja sobre canvas sin leer EXIF — si
|
|
35
|
+
* se le pasara el original, las fotos verticales de teléfono saldrían
|
|
36
|
+
* rotadas. Ver la nota en `generateThumbnail` (types.ts).
|
|
37
|
+
*
|
|
38
|
+
* Nunca es fatal: si falla, el adjunto queda subido sin miniatura y las
|
|
39
|
+
* listas caen al archivo grande. Perder la miniatura no justifica perder la
|
|
40
|
+
* foto que el usuario acaba de subir.
|
|
41
|
+
*/
|
|
42
|
+
private uploadThumbnail;
|
|
29
43
|
private uploadFile;
|
|
30
44
|
remove(id: string): void;
|
|
31
45
|
thumbFailed(id: string): void;
|
|
@@ -3,6 +3,13 @@ export interface AttachmentItem {
|
|
|
3
3
|
file?: File;
|
|
4
4
|
status: 'uploading' | 'ready' | 'error';
|
|
5
5
|
url?: string;
|
|
6
|
+
/**
|
|
7
|
+
* URL de la miniatura, presente solo si se pidió `generateThumbnail` y el
|
|
8
|
+
* archivo es una imagen. Es la que deben usar las LISTAS: `url` apunta al
|
|
9
|
+
* archivo de hasta 1920px, y pedirlo para pintar un recuadro de 84px es la
|
|
10
|
+
* diferencia entre 240KB y 15KB por fila. Ver `generateThumbnail`.
|
|
11
|
+
*/
|
|
12
|
+
thumbnailUrl?: string;
|
|
6
13
|
error?: string;
|
|
7
14
|
isExternal?: boolean;
|
|
8
15
|
}
|
|
@@ -16,6 +23,29 @@ export interface AttachmentUploaderMetadata {
|
|
|
16
23
|
disabled?: boolean;
|
|
17
24
|
/** Compress images before upload (default: true). PDFs are never compressed. */
|
|
18
25
|
compressImages?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Genera y sube además una miniatura cuadrada por cada imagen, y la expone en
|
|
28
|
+
* `AttachmentItem.thumbnailUrl` (default: false).
|
|
29
|
+
*
|
|
30
|
+
* Por qué es opt-in y no el default: sube DOS archivos por imagen en vez de
|
|
31
|
+
* uno, y no todo consumer quiere pagar ese costo (ni tener el archivo extra
|
|
32
|
+
* en su bucket). Actívalo cuando las fotos se muestren en una lista o grilla,
|
|
33
|
+
* que es donde el ahorro es real.
|
|
34
|
+
*
|
|
35
|
+
* La miniatura se genera a partir del archivo YA comprimido, no del original:
|
|
36
|
+
* `compressorjs` normaliza la orientación EXIF al comprimir, así que a esa
|
|
37
|
+
* altura la imagen ya está derecha. Generarla del original la sacaría rotada
|
|
38
|
+
* en las fotos verticales de teléfono (ImageService usa canvas y no lee EXIF).
|
|
39
|
+
*/
|
|
40
|
+
generateThumbnail?: boolean;
|
|
41
|
+
/** Lado de la miniatura en píxeles (default: 320). Cuadrada, recorte centrado. */
|
|
42
|
+
thumbnailSize?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Función de subida de la miniatura. Si se omite, se usa `uploadFn` con un
|
|
45
|
+
* File renombrado a `<nombre>-thumb.jpg` — así el consumer que deriva la ruta
|
|
46
|
+
* del nombre del archivo no necesita hacer nada.
|
|
47
|
+
*/
|
|
48
|
+
thumbnailUploadFn?: (file: File) => Promise<string>;
|
|
19
49
|
/** Upload function. If omitted, falls back to FeedbackService.uploadAttachment. */
|
|
20
50
|
uploadFn?: (file: File) => Promise<string>;
|
|
21
51
|
/** URLs de items ya subidos — se pre-seedean como items 'ready' al montar. */
|
|
@@ -1,83 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { EventEmitter, TemplateRef } from '@angular/core';
|
|
2
|
+
import { ComponentState } from '../../types';
|
|
3
|
+
import { RequestReviewDecision, RequestReviewItem, RequestReviewPanelMetadata } from './types';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
|
-
interface StatusStyle {
|
|
6
|
-
bg: string;
|
|
7
|
-
color: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Config del "entity hero" (bloque opcional arriba del status: ej. animal en
|
|
11
|
-
* Chesed, receta en Mealplan). Agnóstico al dominio — cada app declara de qué
|
|
12
|
-
* campos de `metadata` sacar nombre/imagen y a dónde navegar.
|
|
13
|
-
*/
|
|
14
|
-
export interface RequestReviewEntityHeroConfig {
|
|
15
|
-
labelKey: string;
|
|
16
|
-
nameField: string;
|
|
17
|
-
imageField?: string;
|
|
18
|
-
placeholderIcon?: string;
|
|
19
|
-
canNavigate: (metadata: Record<string, any>) => boolean;
|
|
20
|
-
navigate: (metadata: Record<string, any>) => void;
|
|
21
|
-
}
|
|
22
|
-
export interface RequestReviewPanelConfig {
|
|
23
|
-
/** Namespace i18n donde viven las keys (status_*, approve, reject, submitter, fields, commentsTitle, noComments, submittedAt, statusUpdated, statusError, errorTitle, offlineTitle, offlineHint, retry, viewAnimal). */
|
|
24
|
-
i18nNamespace: string;
|
|
25
|
-
entityHero?: RequestReviewEntityHeroConfig;
|
|
26
|
-
/** Si el usuario actual puede aprobar/rechazar (ej. persona.isPublisher()). */
|
|
27
|
-
canReview: () => boolean;
|
|
28
|
-
/** CTA post-aprobación para quien NO revisa (ej. "ver receta"/"ver animal"). */
|
|
29
|
-
viewEntityCtaLabelKey?: string;
|
|
30
|
-
}
|
|
31
5
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* consumer
|
|
6
|
+
* val-request-review-panel — cola de revisión del factory (ADR-062).
|
|
7
|
+
*
|
|
8
|
+
* Es **presentacional**: no carga datos, no conoce el backend ni el dominio. El
|
|
9
|
+
* consumer le pasa la cola ya normalizada y escucha `(decision)`.
|
|
10
|
+
*
|
|
11
|
+
* ## Por qué el cuerpo va proyectado y no configurado
|
|
12
|
+
*
|
|
13
|
+
* El ADR original proponía describir el cuerpo con arrays de nombres de campo
|
|
14
|
+
* (`fields.display: ['taxId', 'tagsRequested']`). Al implementarlo con los dos
|
|
15
|
+
* consumidores reales delante quedó claro que eso reinventa un motor de
|
|
16
|
+
* templating peor que Angular: Chesed necesita una lista de documentos con
|
|
17
|
+
* iconos y `target="_blank"`, Okhelia un enlace a la receta y un input de
|
|
18
|
+
* comentario. Ninguna lista de strings expresa eso sin agregarle un flag al
|
|
19
|
+
* componente por cada caso nuevo.
|
|
20
|
+
*
|
|
21
|
+
* Entonces el panel es dueño de lo que **de verdad** se repite — la cáscara, el
|
|
22
|
+
* chrome de la card, y la mecánica de decisión — y el cuerpo de cada ítem lo
|
|
23
|
+
* escribe cada vertical en un `ng-template` con su propio HTML.
|
|
24
|
+
*
|
|
25
|
+
* ```html
|
|
26
|
+
* <val-request-review-panel [props]="panelProps()" [itemBody]="body"
|
|
27
|
+
* (decision)="onDecision($event)" />
|
|
28
|
+
*
|
|
29
|
+
* <ng-template #body let-item>
|
|
30
|
+
* <a [routerLink]="['/app/recetas', recipeIdOf(item)]">Ver receta</a>
|
|
31
|
+
* </ng-template>
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ## Rechazo con motivo
|
|
35
|
+
*
|
|
36
|
+
* El flujo de dos pasos (Rechazar → escribir motivo → Confirmar) es del panel,
|
|
37
|
+
* no del consumer, y está activo por defecto. Rechazar de un click deja al
|
|
38
|
+
* solicitante sin saber qué corregir; era el único de los dos paneles que lo
|
|
39
|
+
* tenía bien resuelto y ahora lo heredan los dos.
|
|
36
40
|
*/
|
|
37
|
-
export declare class RequestReviewPanelComponent
|
|
38
|
-
private requests;
|
|
41
|
+
export declare class RequestReviewPanelComponent {
|
|
39
42
|
private i18n;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
private readonly
|
|
52
|
-
readonly
|
|
53
|
-
readonly
|
|
54
|
-
readonly
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
|
|
58
|
-
readonly
|
|
59
|
-
readonly heroConfig: import("@angular/core").Signal<RequestReviewEntityHeroConfig>;
|
|
60
|
-
readonly statusStyle: import("@angular/core").Signal<StatusStyle>;
|
|
61
|
-
readonly heroName: import("@angular/core").Signal<string>;
|
|
62
|
-
readonly heroImage: import("@angular/core").Signal<string>;
|
|
63
|
-
readonly canNavigateHero: import("@angular/core").Signal<boolean>;
|
|
64
|
-
readonly submitterInitial: import("@angular/core").Signal<string>;
|
|
65
|
-
readonly fieldEntries: import("@angular/core").Signal<{
|
|
66
|
-
key: string;
|
|
67
|
-
value: string;
|
|
68
|
-
}[]>;
|
|
69
|
-
readonly errorState: import("@angular/core").Signal<EmptyStateMetadata>;
|
|
43
|
+
readonly props: import("@angular/core").InputSignal<RequestReviewPanelMetadata>;
|
|
44
|
+
/**
|
|
45
|
+
* Cuerpo de cada card. El contexto trae el ítem como `$implicit` y también
|
|
46
|
+
* como `item`, para que el consumer pueda usar `let-item` o `let-x="item"`.
|
|
47
|
+
*/
|
|
48
|
+
itemBody?: TemplateRef<{
|
|
49
|
+
$implicit: RequestReviewItem;
|
|
50
|
+
item: RequestReviewItem;
|
|
51
|
+
}>;
|
|
52
|
+
decision: EventEmitter<RequestReviewDecision>;
|
|
53
|
+
/** ID del ítem cuyo formulario de rechazo está abierto. */
|
|
54
|
+
private readonly _rejectingId;
|
|
55
|
+
readonly rejectingId: import("@angular/core").Signal<string>;
|
|
56
|
+
private readonly _reason;
|
|
57
|
+
readonly reason: import("@angular/core").Signal<string>;
|
|
58
|
+
readonly cfg: import("@angular/core").Signal<RequestReviewPanelMetadata>;
|
|
59
|
+
readonly skeletonRows: import("@angular/core").Signal<number[]>;
|
|
60
|
+
/** Hay una decisión en vuelo: toda la cola se congela. */
|
|
61
|
+
readonly isBusy: import("@angular/core").Signal<boolean>;
|
|
70
62
|
constructor();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
63
|
+
protected t(key: string): string;
|
|
64
|
+
/** El ítem en vuelo muestra WORKING; los demás quedan deshabilitados. */
|
|
65
|
+
private stateFor;
|
|
66
|
+
protected approveState(item: RequestReviewItem): ComponentState;
|
|
67
|
+
/**
|
|
68
|
+
* Confirmar el rechazo exige motivo cuando `requireRejectReason` está activo
|
|
69
|
+
* (el default). Es el freno que evita la notificación de rechazo vacía.
|
|
70
|
+
*/
|
|
71
|
+
protected confirmRejectState(item: RequestReviewItem): ComponentState;
|
|
72
|
+
protected onReasonInput(event: Event): void;
|
|
73
|
+
protected startReject(item: RequestReviewItem): void;
|
|
74
|
+
protected cancelReject(): void;
|
|
75
|
+
protected confirmReject(item: RequestReviewItem): void;
|
|
76
|
+
protected approve(item: RequestReviewItem): void;
|
|
80
77
|
static ɵfac: i0.ɵɵFactoryDeclaration<RequestReviewPanelComponent, never>;
|
|
81
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<RequestReviewPanelComponent, "val-request-review-panel", never, { "
|
|
78
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RequestReviewPanelComponent, "val-request-review-panel", never, { "props": { "alias": "props"; "required": true; "isSignal": true; }; "itemBody": { "alias": "itemBody"; "required": false; }; }, { "decision": "decision"; }, never, never, true, never>;
|
|
82
79
|
}
|
|
83
|
-
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { EmptyStateMetadata } from '../../molecules/empty-state/types';
|
|
2
|
+
/**
|
|
3
|
+
* Un ítem de la cola de revisión, ya normalizado por el consumer.
|
|
4
|
+
*
|
|
5
|
+
* Deliberadamente pobre: id, título y unas líneas de metadata. Todo lo que sea
|
|
6
|
+
* del dominio (el RUT del criadero, los tags de la receta, los documentos
|
|
7
|
+
* adjuntos) va en el cuerpo proyectado, no acá — la lib no aprende qué es una
|
|
8
|
+
* receta ni un criadero.
|
|
9
|
+
*/
|
|
10
|
+
export interface RequestReviewItem {
|
|
11
|
+
/** Identidad del ítem. Es lo que viaja en la decisión. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Encabezado de la card. */
|
|
14
|
+
title: string;
|
|
15
|
+
/**
|
|
16
|
+
* Líneas bajo el título (fecha de envío, identificador, estado). Texto ya
|
|
17
|
+
* resuelto y formateado por el consumer: la lib no traduce ni formatea fechas.
|
|
18
|
+
*/
|
|
19
|
+
meta?: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Lo que emite el panel cuando el revisor decide. */
|
|
22
|
+
export interface RequestReviewDecision {
|
|
23
|
+
itemId: string;
|
|
24
|
+
approved: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Motivo del rechazo. Presente solo al rechazar con `requireRejectReason`
|
|
27
|
+
* activo (el default), donde el panel garantiza que no viene vacío.
|
|
28
|
+
*/
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RequestReviewPanelMetadata {
|
|
32
|
+
/** La cola. Vacía + `loading: false` muestra el estado vacío. */
|
|
33
|
+
items: RequestReviewItem[];
|
|
34
|
+
/** Muestra el skeleton en vez de la lista. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
/** Filas del skeleton (default: 3). */
|
|
37
|
+
skeletonRows?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Estado de error de la carga. Tiene prioridad sobre todo lo demás — un panel
|
|
40
|
+
* que no pudo cargar no debe mostrar "no hay nada pendiente", que es una
|
|
41
|
+
* afirmación distinta y falsa.
|
|
42
|
+
*/
|
|
43
|
+
errorState?: EmptyStateMetadata | null;
|
|
44
|
+
/** Estado cuando la cola está vacía de verdad. */
|
|
45
|
+
emptyState?: EmptyStateMetadata;
|
|
46
|
+
/**
|
|
47
|
+
* Línea de conteo sobre la lista, ya compuesta por el consumer
|
|
48
|
+
* ("3 pendientes"). Sin ella no se muestra nada.
|
|
49
|
+
*/
|
|
50
|
+
countLabel?: string;
|
|
51
|
+
/**
|
|
52
|
+
* ID del ítem con una decisión en vuelo. Ese ítem muestra su acción en
|
|
53
|
+
* `WORKING` y el resto queda deshabilitado: dos decisiones simultáneas sobre
|
|
54
|
+
* la misma cola es justo lo que no se quiere.
|
|
55
|
+
*/
|
|
56
|
+
busyItemId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Exigir un motivo escrito para rechazar (default: **true**).
|
|
59
|
+
*
|
|
60
|
+
* Es el default a propósito. Rechazar sin explicación deja al solicitante sin
|
|
61
|
+
* saber qué corregir, y la notificación de decisión sale vacía. Ponerlo en
|
|
62
|
+
* `false` es una decisión explícita del consumer, no un olvido.
|
|
63
|
+
*/
|
|
64
|
+
requireRejectReason?: boolean;
|
|
65
|
+
/** Etiquetas. Todas opcionales — la lib trae defaults es/en. */
|
|
66
|
+
approveLabel?: string;
|
|
67
|
+
rejectLabel?: string;
|
|
68
|
+
cancelLabel?: string;
|
|
69
|
+
confirmRejectLabel?: string;
|
|
70
|
+
rejectReasonLabel?: string;
|
|
71
|
+
rejectReasonPlaceholder?: string;
|
|
72
|
+
rejectReasonHint?: string;
|
|
73
|
+
}
|
|
@@ -3,7 +3,7 @@ import { FormControl } from '@angular/forms';
|
|
|
3
3
|
import { ButtonMetadata } from '../../types';
|
|
4
4
|
import type { Color } from '@ionic/core';
|
|
5
5
|
import { EditableFieldDef, FieldSchemaEditorMetadata } from '../field-schema-editor/types';
|
|
6
|
-
import { SurveyBuilderMetadata, SurveyBuilderSaveEvent } from './types';
|
|
6
|
+
import { SurveyBuilderMetadata, SurveyBuilderSaveEvent, SurveyDraftEvent } from './types';
|
|
7
7
|
import * as i0 from "@angular/core";
|
|
8
8
|
/**
|
|
9
9
|
* val-survey-builder
|
|
@@ -25,6 +25,13 @@ import * as i0 from "@angular/core";
|
|
|
25
25
|
export declare class SurveyBuilderComponent implements OnInit {
|
|
26
26
|
props: SurveyBuilderMetadata;
|
|
27
27
|
readonly saved: EventEmitter<SurveyBuilderSaveEvent>;
|
|
28
|
+
/**
|
|
29
|
+
* Emite lo escrito, ya validado, SIN guardarlo. Solo con `props.persist:
|
|
30
|
+
* false` — ver el comentario de ese campo: cuando la app tiene endpoint
|
|
31
|
+
* propio, guardar desde acá se salta la validación de la entidad dueña y el
|
|
32
|
+
* permiso de la vertical.
|
|
33
|
+
*/
|
|
34
|
+
readonly draft: EventEmitter<SurveyDraftEvent>;
|
|
28
35
|
private readonly i18n;
|
|
29
36
|
private readonly requests;
|
|
30
37
|
private readonly formBuilder;
|
|
@@ -61,5 +68,5 @@ export declare class SurveyBuilderComponent implements OnInit {
|
|
|
61
68
|
save(): Promise<void>;
|
|
62
69
|
private rowFromField;
|
|
63
70
|
static ɵfac: i0.ɵɵFactoryDeclaration<SurveyBuilderComponent, never>;
|
|
64
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SurveyBuilderComponent, "val-survey-builder", never, { "props": { "alias": "props"; "required": false; }; }, { "saved": "saved"; }, never, never, true, never>;
|
|
71
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SurveyBuilderComponent, "val-survey-builder", never, { "props": { "alias": "props"; "required": false; }; }, { "saved": "saved"; "draft": "draft"; }, never, never, true, never>;
|
|
65
72
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityRef, RequestTypeConfig, RequestVisibility } from '../../../services/requests/types';
|
|
1
|
+
import { EntityRef, FieldSchemaDef, RequestTypeConfig, RequestVisibility } from '../../../services/requests/types';
|
|
2
2
|
/**
|
|
3
3
|
* Tipos de pregunta que ofrece una encuesta.
|
|
4
4
|
*
|
|
@@ -46,7 +46,34 @@ export interface SurveyBuilderMetadata {
|
|
|
46
46
|
allowAnonymous?: boolean;
|
|
47
47
|
/** Acota el catálogo de `SURVEY_QUESTION_TYPES` si una app quiere ofrecer menos. */
|
|
48
48
|
questionTypes?: string[];
|
|
49
|
+
/**
|
|
50
|
+
* Si el builder guarda por su cuenta contra `/v2/request-types`.
|
|
51
|
+
*
|
|
52
|
+
* Default `true` por compatibilidad, pero **una app con endpoint propio debe
|
|
53
|
+
* pasar `false`** y guardar ella: el endpoint de la vertical es el que valida
|
|
54
|
+
* la entidad dueña, evita que dos encuestas de distinto evento colisionen en
|
|
55
|
+
* el mismo `typeId`, y autoriza con SU permiso. Con `false`, `save()` valida
|
|
56
|
+
* y emite `draft` sin tocar la red.
|
|
57
|
+
*/
|
|
58
|
+
persist?: boolean;
|
|
49
59
|
}
|
|
50
60
|
export interface SurveyBuilderSaveEvent {
|
|
51
61
|
typeConfig: RequestTypeConfig;
|
|
52
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Lo que se escribió, sin persistir.
|
|
65
|
+
*
|
|
66
|
+
* Existe porque **el builder no siempre debe guardar por su cuenta**. Cuando la
|
|
67
|
+
* app tiene su propio endpoint de dominio (el de bingo valida el evento, acota
|
|
68
|
+
* el `typeId` para que dos eventos no colisionen y autoriza con el permiso de
|
|
69
|
+
* la vertical en vez de `apps:manage`), guardar desde acá se salta todo eso.
|
|
70
|
+
*
|
|
71
|
+
* Los tres campos son exactamente los de `CreateEventSurveyRequest` en
|
|
72
|
+
* `services/bingo/survey.go` — no es casualidad: es el contrato que la app
|
|
73
|
+
* necesita reenviar.
|
|
74
|
+
*/
|
|
75
|
+
export interface SurveyDraftEvent {
|
|
76
|
+
label: string;
|
|
77
|
+
subtitle: string;
|
|
78
|
+
questions: FieldSchemaDef[];
|
|
79
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EnvironmentProviders, InjectionToken } from '@angular/core';
|
|
2
|
+
import { ValtechPaymentsConfig } from './types';
|
|
3
|
+
/** Token de inyección de la configuración de pagos. */
|
|
4
|
+
export declare const VALTECH_PAYMENTS_CONFIG: InjectionToken<ValtechPaymentsConfig>;
|
|
5
|
+
export declare const DEFAULT_PAYMENTS_CONFIG: Partial<ValtechPaymentsConfig>;
|
|
6
|
+
/**
|
|
7
|
+
* Provee el cliente de pasarelas de pago.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* providePaymentsGateway({ apiUrl: environment.apiUrl }),
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function providePaymentsGateway(config: ValtechPaymentsConfig): EnvironmentProviders;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { ConnectionStatusResponse, OAuthAuthorizeURLResponse } from './types';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Cliente del onboarding de pasarelas de pago (ADR-028).
|
|
6
|
+
*
|
|
7
|
+
* Conecta la cuenta de UNA ORGANIZACIÓN con una pasarela, para que el dinero
|
|
8
|
+
* le llegue directo. Ese es el punto: si el dinero pasara por una cuenta de
|
|
9
|
+
* Valtech, Valtech sería un PSP regulado por la CMF (ver ADR-028). Por eso
|
|
10
|
+
* todas las operaciones son por `orgId`, y no existe acá un modo "cuenta de la
|
|
11
|
+
* plataforma".
|
|
12
|
+
*
|
|
13
|
+
* Vive en la lib y no en una app porque el flujo es idéntico en todas: Bingo
|
|
14
|
+
* lo usa para que cada colegio cobre sus cartones, y Chesed para que cada
|
|
15
|
+
* refugio reciba aportes. Lo que cambia entre apps es qué se cobra, no cómo se
|
|
16
|
+
* conecta la cuenta.
|
|
17
|
+
*
|
|
18
|
+
* Autenticación: no agrega el Bearer a mano — lo pone el interceptor de
|
|
19
|
+
* `provideValtechAuth`. Errores: no se capturan acá; los maneja la pantalla.
|
|
20
|
+
*/
|
|
21
|
+
export declare class PaymentsGatewayService {
|
|
22
|
+
private http;
|
|
23
|
+
private config;
|
|
24
|
+
private get base();
|
|
25
|
+
/**
|
|
26
|
+
* Estado de conexión de la organización con una pasarela.
|
|
27
|
+
*
|
|
28
|
+
* Es lo que decide si la app puede ofrecer el cobro: `connected` habilita,
|
|
29
|
+
* `expired` y `revoked` NO. Mostrar un botón de pago con un token revocado
|
|
30
|
+
* lleva al usuario a un cobro que falla después de que ya decidió pagar.
|
|
31
|
+
*/
|
|
32
|
+
getConnectionStatus(orgId: string, gateway: string): Observable<ConnectionStatusResponse>;
|
|
33
|
+
/**
|
|
34
|
+
* URL de autorización OAuth, con su `state` CSRF.
|
|
35
|
+
*
|
|
36
|
+
* El caller guarda el `state` antes de redirigir y lo compara al volver: sin
|
|
37
|
+
* eso, cualquiera podría completar la conexión de otra organización.
|
|
38
|
+
*/
|
|
39
|
+
getOAuthAuthorizeURL(orgId: string, gateway: string): Observable<OAuthAuthorizeURLResponse>;
|
|
40
|
+
/** Desconecta la credencial de la organización para esa pasarela. */
|
|
41
|
+
disconnect(orgId: string, gateway: string): Observable<{
|
|
42
|
+
operationId: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Guarda una credencial de acceso directo (pasarelas sin OAuth, ej. Khipu).
|
|
46
|
+
* El token es el secreto del cobrador.
|
|
47
|
+
*/
|
|
48
|
+
setCredential(orgId: string, gateway: string, accessToken: string): Observable<{
|
|
49
|
+
operationId: string;
|
|
50
|
+
}>;
|
|
51
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PaymentsGatewayService, never>;
|
|
52
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<PaymentsGatewayService>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contratos del onboarding de pasarelas de pago (ADR-028).
|
|
3
|
+
*
|
|
4
|
+
* Espejo reducido del backend: solo lo que una app necesita para conectar la
|
|
5
|
+
* cuenta de una organización y saber si puede cobrar.
|
|
6
|
+
*/
|
|
7
|
+
/** Estado del ciclo de vida de una credencial OAuth. */
|
|
8
|
+
export type OAuthStatus = 'never' | 'connected' | 'expired' | 'revoked';
|
|
9
|
+
/**
|
|
10
|
+
* Respuesta de `connection-status`. No expone el token: solo un enmascarado
|
|
11
|
+
* informativo, porque la UI nunca necesita el secreto.
|
|
12
|
+
*/
|
|
13
|
+
export interface ConnectionStatusResponse {
|
|
14
|
+
operationId: string;
|
|
15
|
+
connected: boolean;
|
|
16
|
+
oauthStatus: OAuthStatus;
|
|
17
|
+
connectedAt?: string;
|
|
18
|
+
maskedToken?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Respuesta de `oauth/authorize-url`. */
|
|
21
|
+
export interface OAuthAuthorizeURLResponse {
|
|
22
|
+
operationId: string;
|
|
23
|
+
authorizeUrl: string;
|
|
24
|
+
/** CSRF. El caller lo guarda antes de redirigir y lo compara al volver. */
|
|
25
|
+
state: string;
|
|
26
|
+
}
|
|
27
|
+
/** Configuración del cliente de pagos. */
|
|
28
|
+
export interface ValtechPaymentsConfig {
|
|
29
|
+
/** Base del backend, sin barra final (ej. `https://api-dev.myvaltech.com`). */
|
|
30
|
+
apiUrl: string;
|
|
31
|
+
/** Prefijo de la API de pagos. Default `/v1/payments`. */
|
|
32
|
+
paymentsPrefix?: string;
|
|
33
|
+
}
|
package/lib/version.d.ts
CHANGED
package/package.json
CHANGED
package/public-api.d.ts
CHANGED
|
@@ -242,6 +242,8 @@ export * from './lib/components/organisms/survey-response/survey-response.compon
|
|
|
242
242
|
export * from './lib/components/organisms/survey-response/types';
|
|
243
243
|
export * from './lib/components/organisms/field-schema-editor/field-schema-editor.component';
|
|
244
244
|
export * from './lib/components/organisms/field-schema-editor/types';
|
|
245
|
+
export * from './lib/components/organisms/request-review-panel/request-review-panel.component';
|
|
246
|
+
export * from './lib/components/organisms/request-review-panel/types';
|
|
245
247
|
export * from './lib/components/organisms/survey-builder/survey-builder.component';
|
|
246
248
|
export * from './lib/components/organisms/survey-builder/types';
|
|
247
249
|
export * from './lib/components/molecules/empty-state/empty-state.component';
|
|
@@ -269,7 +271,6 @@ export * from './lib/components/organisms/share-profile-modal/share-profile-moda
|
|
|
269
271
|
export * from './lib/components/organisms/share-profile-modal/types';
|
|
270
272
|
export * from './lib/components/organisms/fun-modal/fun-modal.component';
|
|
271
273
|
export * from './lib/components/organisms/request-modal/request-modal.component';
|
|
272
|
-
export * from './lib/components/organisms/request-review-panel/request-review-panel.component';
|
|
273
274
|
export * from './lib/components/organisms/fun-modal/types';
|
|
274
275
|
export * from './lib/components/organisms/cookie-banner/cookie-banner.component';
|
|
275
276
|
export * from './lib/components/organisms/cookie-banner/types';
|
|
@@ -499,6 +500,7 @@ export * from './lib/components/organisms/verify-view/verify-view.component';
|
|
|
499
500
|
export * from './lib/components/organisms/verify-view/types';
|
|
500
501
|
export * from './lib/services/content-platform';
|
|
501
502
|
export * from './lib/services/feedback';
|
|
503
|
+
export * from './lib/services/payments';
|
|
502
504
|
export * from './lib/services/whatsapp';
|
|
503
505
|
export * from './lib/components/organisms/whatsapp-fab/whatsapp-fab.component';
|
|
504
506
|
export * from './lib/services/phone-format/phone-format.service';
|