valtech-components 4.0.982 → 4.0.983

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.
Files changed (24) hide show
  1. package/esm2022/lib/components/atoms/button/button.component.mjs +3 -1
  2. package/esm2022/lib/components/organisms/field-schema-editor/field-schema-editor.component.mjs +332 -0
  3. package/esm2022/lib/components/organisms/field-schema-editor/field-schema-editor.i18n.mjs +71 -0
  4. package/esm2022/lib/components/organisms/field-schema-editor/types.mjs +2 -0
  5. package/esm2022/lib/components/organisms/survey-builder/survey-builder.component.mjs +265 -136
  6. package/esm2022/lib/components/organisms/survey-builder/survey-builder.i18n.mjs +19 -7
  7. package/esm2022/lib/components/organisms/survey-builder/types.mjs +31 -2
  8. package/esm2022/lib/components/organisms/survey-response/survey-response.component.mjs +294 -26
  9. package/esm2022/lib/components/types.mjs +1 -1
  10. package/esm2022/lib/version.mjs +2 -2
  11. package/esm2022/public-api.mjs +3 -1
  12. package/fesm2022/valtech-components.mjs +1052 -231
  13. package/fesm2022/valtech-components.mjs.map +1 -1
  14. package/lib/components/organisms/article/article.component.d.ts +1 -0
  15. package/lib/components/organisms/field-schema-editor/field-schema-editor.component.d.ts +90 -0
  16. package/lib/components/organisms/field-schema-editor/field-schema-editor.i18n.d.ts +2 -0
  17. package/lib/components/organisms/field-schema-editor/types.d.ts +60 -0
  18. package/lib/components/organisms/survey-builder/survey-builder.component.d.ts +26 -14
  19. package/lib/components/organisms/survey-builder/types.d.ts +23 -5
  20. package/lib/components/organisms/survey-response/survey-response.component.d.ts +42 -1
  21. package/lib/components/types.d.ts +6 -0
  22. package/lib/version.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/public-api.d.ts +2 -0
@@ -107,6 +107,7 @@ export declare class ArticleComponent implements OnInit, AfterViewInit {
107
107
  fill?: "default" | "clear" | "outline" | "solid" | "tint";
108
108
  type: "reset" | "submit" | "button";
109
109
  token?: string;
110
+ ariaLabel?: string;
110
111
  ref?: any;
111
112
  handler?: (value: any) => any;
112
113
  };
@@ -0,0 +1,90 @@
1
+ import { EventEmitter, OnInit } from '@angular/core';
2
+ import { FormControl } from '@angular/forms';
3
+ import { FieldOption } from '../../../services/forms/types';
4
+ import { InputMetadata } from '../../types';
5
+ import { EditableFieldDef, FieldSchemaEditorMetadata, FieldSchemaEditorPreset } from './types';
6
+ import * as i0 from "@angular/core";
7
+ /**
8
+ * Tipos cuyo valor sale de una lista cerrada que define quien arma el
9
+ * formulario — sin al menos una opción, el campo no se puede responder.
10
+ * Espejo de `fieldRequiresOptions` que vivía duplicado en bingo.
11
+ */
12
+ export declare const FIELD_TYPES_WITH_OPTIONS: string[];
13
+ /**
14
+ * val-field-schema-editor
15
+ *
16
+ * Edita UN campo de un formulario dinámico: texto, tipo, placeholder, nota,
17
+ * obligatoriedad y opciones. Es la pieza que faltaba en la lib — hasta ahora
18
+ * el único editor de campos con este nivel vivía dentro de bingo
19
+ * (`checkout-field-modal.component.ts`), invisible para el resto del factory,
20
+ * y `val-survey-builder` había terminado con una versión pobre de lo mismo
21
+ * (dos tipos fijos, sin placeholder ni opciones).
22
+ *
23
+ * Es agnóstico de dominio a propósito:
24
+ * - el **catálogo de tipos** (`allowedTypes`) lo decide el consumer, porque es
25
+ * él quien sabe qué sabe renderizar su formulario;
26
+ * - los **presets** los pasa el consumer, porque "correo del comprador" es de
27
+ * checkout y no significa nada en una encuesta.
28
+ *
29
+ * No trae cáscara de modal: el consumer lo envuelve en `val-modal-layout` (o
30
+ * en lo que quiera) y dispara `submit()` desde su propio botón, igual que hace
31
+ * `survey-create-modal` con `val-survey-builder`.
32
+ */
33
+ export declare class FieldSchemaEditorComponent implements OnInit {
34
+ props: FieldSchemaEditorMetadata;
35
+ readonly save: EventEmitter<EditableFieldDef>;
36
+ private readonly i18n;
37
+ private readonly formSchemas;
38
+ readonly labelControl: FormControl<string>;
39
+ readonly placeholderControl: FormControl<string>;
40
+ readonly hintControl: FormControl<string>;
41
+ readonly requiredControl: FormControl<boolean>;
42
+ readonly typeControl: FormControl<string>;
43
+ readonly options: import("@angular/core").WritableSignal<FieldOption[]>;
44
+ readonly validationError: import("@angular/core").WritableSignal<string>;
45
+ readonly selectedPresetId: import("@angular/core").WritableSignal<string>;
46
+ /** Se refresca con cada cambio de tipo — `needsOptions` no puede leer el control directo. */
47
+ private readonly currentType;
48
+ /** Una vez que se toca el token a mano (o se edita un campo ya guardado) deja de derivarse del label. */
49
+ private nameLocked;
50
+ private name;
51
+ private fieldId;
52
+ constructor();
53
+ ngOnInit(): void;
54
+ t(key: string): string;
55
+ state(): import("../../types").ComponentState;
56
+ showPresets(): boolean;
57
+ needsOptions(): boolean;
58
+ typeLabel(type: string): string;
59
+ readonly typeSelectProps: import("@angular/core").Signal<Partial<InputMetadata>>;
60
+ readonly optionsEditorProps: import("@angular/core").Signal<{
61
+ title: string;
62
+ hint: string;
63
+ addLabel: string;
64
+ optionsLabel: string;
65
+ placeholder: string;
66
+ emptyText: string;
67
+ removeOptionLabel: string;
68
+ reorderOptionLabel: string;
69
+ state: import("../../types").ComponentState;
70
+ options: FieldOption[];
71
+ }>;
72
+ selectPreset(preset: FieldSchemaEditorPreset): void;
73
+ /**
74
+ * Valida y emite. Público a propósito: lo llama el consumer desde el botón
75
+ * de su propia cáscara (modal, página o acordeón).
76
+ */
77
+ submit(): boolean;
78
+ /** El campo tal como está en pantalla, sin validar. */
79
+ value(): EditableFieldDef;
80
+ private allowedTypes;
81
+ /**
82
+ * Un tipo fuera del catálogo del consumer cae al primero permitido. Pasa al
83
+ * editar un campo guardado con un tipo que la app ya no ofrece (bingo lo
84
+ * hacía con ATTACHMENT cuando el medio de pago no lo admite): sin esto, el
85
+ * select queda en un valor que no está en su lista y se ve vacío.
86
+ */
87
+ private coerceType;
88
+ static ɵfac: i0.ɵɵFactoryDeclaration<FieldSchemaEditorComponent, never>;
89
+ static ɵcmp: i0.ɵɵComponentDeclaration<FieldSchemaEditorComponent, "val-field-schema-editor", never, { "props": { "alias": "props"; "required": false; }; }, { "save": "save"; }, never, never, true, never>;
90
+ }
@@ -0,0 +1,2 @@
1
+ import { LanguagesContent } from '../../../services/i18n';
2
+ export declare const FIELD_SCHEMA_EDITOR_I18N: LanguagesContent;
@@ -0,0 +1,60 @@
1
+ import { ComponentState } from '../../types';
2
+ import { FieldOption } from '../../../services/forms/types';
3
+ /**
4
+ * Un campo en edición — la forma mutable con la que trabaja el editor antes de
5
+ * convertirse en `FieldSchemaDef` vía `FormSchemaBuilderService.createFieldSchema`.
6
+ *
7
+ * No es `FieldSchemaDef` a propósito: ese tipo carga `labelKey`, `order`,
8
+ * `validators` y los `*Es/*En` de compatibilidad, ninguno de los cuales edita
9
+ * una persona. El editor trabaja con lo que se escribe en pantalla y deja que
10
+ * el builder derive el resto.
11
+ */
12
+ export interface EditableFieldDef {
13
+ /** Id local estable — sirve para `track` y para reemplazar en la lista al editar. */
14
+ id: string;
15
+ /** Token del campo (derivado del label mientras no se edite a mano). */
16
+ name: string;
17
+ /** Texto que escribe la organización. No se traduce (regla #4 de CLAUDE.md). */
18
+ label: string;
19
+ placeholderText: string;
20
+ hintText: string;
21
+ type: string;
22
+ required: boolean;
23
+ options?: FieldOption[];
24
+ }
25
+ /**
26
+ * Un punto de partida ofrecido al crear un campo. Los define el consumer, no
27
+ * el editor: los presets de un checkout (correo, teléfono, curso) no tienen
28
+ * nada que ver con los de una encuesta.
29
+ */
30
+ export interface FieldSchemaEditorPreset {
31
+ id: string;
32
+ /** Título de la tarjeta del preset. */
33
+ label: string;
34
+ /** Bajada de la tarjeta. Opcional. */
35
+ description?: string;
36
+ /** Valores con los que se rellena el editor al elegirlo. */
37
+ field: Omit<EditableFieldDef, 'id'>;
38
+ }
39
+ export interface FieldSchemaEditorMetadata {
40
+ /** 'edit' oculta los presets y no re-deriva el `name` desde el label. */
41
+ mode?: 'new' | 'edit';
42
+ /** Campo a editar. Ausente = campo nuevo. */
43
+ field?: EditableFieldDef | null;
44
+ /**
45
+ * Catálogo de tipos que este consumer ofrece. Obligatorio y acotado a
46
+ * propósito: quien usa el editor decide qué tipos tienen sentido en su
47
+ * dominio (una encuesta pública no ofrece ATTACHMENT, un checkout no ofrece
48
+ * EMOJI_RATING) y, sobre todo, garantiza que su renderer sepa dibujarlos.
49
+ */
50
+ allowedTypes: string[];
51
+ /** Etiqueta visible por tipo. Si falta una, cae al default i18n del editor. */
52
+ typeLabels?: Record<string, string>;
53
+ presets?: FieldSchemaEditorPreset[];
54
+ presetsTitle?: string;
55
+ /** Default true. */
56
+ showPlaceholder?: boolean;
57
+ /** Default true. */
58
+ showHint?: boolean;
59
+ state?: ComponentState;
60
+ }
@@ -1,6 +1,9 @@
1
1
  import { EventEmitter, OnInit } from '@angular/core';
2
2
  import { FormControl } from '@angular/forms';
3
- import { SurveyBuilderMetadata, SurveyBuilderSaveEvent, SurveyQuestionRow } from './types';
3
+ import { ButtonMetadata } from '../../types';
4
+ import type { Color } from '@ionic/core';
5
+ import { EditableFieldDef, FieldSchemaEditorMetadata } from '../field-schema-editor/types';
6
+ import { SurveyBuilderMetadata, SurveyBuilderSaveEvent } from './types';
4
7
  import * as i0 from "@angular/core";
5
8
  /**
6
9
  * val-survey-builder
@@ -10,11 +13,14 @@ import * as i0 from "@angular/core";
10
13
  * true` (cada respuesta se agrega, no se revisa una por una) y el `EntityRef`
11
14
  * que le pasa el consumer (ej. el evento de bingo dueño de la encuesta).
12
15
  *
13
- * v1 solo ofrece los dos tipos de pregunta que `val-survey-response` sabe
14
- * renderizar: emoji-rating y texto libre. Reusa `RequestFormBuilderService`
15
- * (`toToken`/`uniqueFieldName`/`createFieldSchema`) para el nombre de campo
16
- * es la misma primitiva que ya arma el formulario dinámico de `request`, no
17
- * una reimplementación.
16
+ * Cada pregunta se edita con `val-field-schema-editor`, el mismo editor que usa
17
+ * el checkout de bingo de ahí que una pregunta tenga tipo, placeholder, nota
18
+ * y opciones, no solo texto. La v1 de este componente tenía su propio editor
19
+ * pobre (dos tipos, sin opciones); la Fase 5 del ADR-092 lo reemplazó por el
20
+ * organism compartido en vez de mejorarlo por duplicado.
21
+ *
22
+ * El catálogo que ofrece sale de `SURVEY_QUESTION_TYPES` — acotado a lo que
23
+ * `val-survey-response` sabe renderizar, no a lo que el editor sabe editar.
18
24
  */
19
25
  export declare class SurveyBuilderComponent implements OnInit {
20
26
  props: SurveyBuilderMetadata;
@@ -23,21 +29,27 @@ export declare class SurveyBuilderComponent implements OnInit {
23
29
  private readonly requests;
24
30
  private readonly formBuilder;
25
31
  private readonly errors;
26
- readonly emojiType = "EMOJI_RATING";
27
32
  readonly titleControl: FormControl<string>;
28
33
  readonly subtitleControl: FormControl<string>;
29
- readonly questions: import("@angular/core").WritableSignal<SurveyQuestionRow[]>;
34
+ readonly questions: import("@angular/core").WritableSignal<EditableFieldDef[]>;
35
+ readonly editingId: import("@angular/core").WritableSignal<string>;
36
+ readonly validationError: import("@angular/core").WritableSignal<string>;
30
37
  readonly saving: import("@angular/core").WritableSignal<boolean>;
31
- private readonly rowControls;
32
38
  constructor();
33
39
  ngOnInit(): Promise<void>;
34
- private rowFromField;
35
- rowLabelControl(row: SurveyQuestionRow): FormControl<string>;
40
+ t(key: string): string;
41
+ typeLabel(type: string): string;
42
+ questionTypes(): string[];
43
+ editorProps(row: EditableFieldDef): FieldSchemaEditorMetadata;
44
+ iconAction(token: string, icon: string, label: string, disabled?: boolean, color?: Color): ButtonMetadata;
36
45
  addQuestion(): void;
37
- removeQuestion(row: SurveyQuestionRow): void;
38
- setType(row: SurveyQuestionRow, type: SurveyQuestionRow['type']): void;
46
+ toggleEdit(row: EditableFieldDef): void;
47
+ removeQuestion(row: EditableFieldDef): void;
48
+ duplicate(row: EditableFieldDef): void;
49
+ move(index: number, delta: number): void;
50
+ applyQuestion(field: EditableFieldDef): void;
39
51
  save(): Promise<void>;
40
- t(key: string): string;
52
+ private rowFromField;
41
53
  static ɵfac: i0.ɵɵFactoryDeclaration<SurveyBuilderComponent, never>;
42
54
  static ɵcmp: i0.ɵɵComponentDeclaration<SurveyBuilderComponent, "val-survey-builder", never, { "props": { "alias": "props"; "required": false; }; }, { "saved": "saved"; }, never, never, true, never>;
43
55
  }
@@ -1,12 +1,28 @@
1
1
  import { EntityRef, RequestTypeConfig, RequestVisibility } from '../../../services/requests/types';
2
2
  /**
3
- * Una pregunta en construcción antes de convertirse en `FieldSchemaDef`.
4
- * v1 solo soporta estos dos tipos (ADR-092) — el mismo par que sabe
5
- * renderizar `val-survey-response`. Ampliar acá implica ampliar también el
6
- * renderer de respuesta, no es un cambio de un solo lado.
3
+ * Tipos de pregunta que ofrece una encuesta.
4
+ *
5
+ * El corte no es estético: **todo tipo que esta lista ofrezca, `val-survey-response`
6
+ * tiene que saber renderizarlo**, o la encuesta se guarda bien y se responde mal
7
+ * (un rating dibujado como caja de texto). Ampliar acá obliga a ampliar el
8
+ * renderer en la misma entrega.
9
+ *
10
+ * Los seis primeros son de respuesta cerrada — `closedAnswerTypes` en
11
+ * `backend/go/services/request/aggregate.go` los cuenta por valor en el
12
+ * dashboard. Los seis últimos son abiertos: el dashboard lista sus respuestas.
13
+ *
14
+ * `FILE`/`ATTACHMENT` quedan deliberadamente fuera: una encuesta es pública y
15
+ * anónima por default, y aceptar subidas ahí es superficie de abuso sin un caso
16
+ * de uso que lo pida.
17
+ */
18
+ export declare const SURVEY_QUESTION_TYPES: string[];
19
+ /**
20
+ * @deprecated Quedó de la v1 del builder (ADR-092 Fase 2), cuando una pregunta
21
+ * era solo label + uno de dos tipos. Hoy una pregunta es un `EditableFieldDef`
22
+ * completo — ver `val-field-schema-editor`. No se usa en ningún lado; se
23
+ * conserva un ciclo para no romper un import perdido.
7
24
  */
8
25
  export interface SurveyQuestionRow {
9
- /** Id local, solo para `track` en el template — no viaja al backend. */
10
26
  id: string;
11
27
  label: string;
12
28
  type: 'TEXTAREA' | 'EMOJI_RATING';
@@ -22,6 +38,8 @@ export interface SurveyBuilderMetadata {
22
38
  visibility?: RequestVisibility;
23
39
  /** Default true. */
24
40
  allowAnonymous?: boolean;
41
+ /** Acota el catálogo de `SURVEY_QUESTION_TYPES` si una app quiere ofrecer menos. */
42
+ questionTypes?: string[];
25
43
  }
26
44
  export interface SurveyBuilderSaveEvent {
27
45
  typeConfig: RequestTypeConfig;
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter, OnInit } from '@angular/core';
2
2
  import { FormControl } from '@angular/forms';
3
- import { RequestTypeConfig } from '../../../services/requests/types';
3
+ import { FieldSchemaDef, RequestTypeConfig } from '../../../services/requests/types';
4
+ import { InputMetadata, InputOption, InputType } from '../../types';
4
5
  import { EmptyStateMetadata } from '../../molecules/empty-state/types';
5
6
  import { SurveyResponseMetadata, SurveyResponseSubmitEvent } from './types';
6
7
  import * as i0 from "@angular/core";
@@ -33,20 +34,60 @@ export declare class SurveyResponseComponent implements OnInit {
33
34
  private readonly requests;
34
35
  private readonly errors;
35
36
  readonly emojiType = "EMOJI_RATING";
37
+ readonly selectType = InputType.SELECT;
36
38
  readonly loading: import("@angular/core").WritableSignal<boolean>;
37
39
  readonly loadError: import("@angular/core").WritableSignal<boolean>;
38
40
  readonly sent: import("@angular/core").WritableSignal<boolean>;
39
41
  readonly sending: import("@angular/core").WritableSignal<boolean>;
40
42
  readonly typeConfig: import("@angular/core").WritableSignal<RequestTypeConfig>;
43
+ readonly multiSelectError: import("@angular/core").WritableSignal<boolean>;
41
44
  readonly nameControl: FormControl<string>;
42
45
  readonly emailControl: FormControl<string>;
43
46
  private form;
47
+ /**
48
+ * Una casilla por opción de cada pregunta MULTI_SELECT, indexada
49
+ * `"<pregunta>::<opcion>"`. Viven fuera del `form` porque el valor que viaja
50
+ * al backend es UNA lista por pregunta, no N booleanos: se arma al enviar.
51
+ */
52
+ private readonly multiControls;
44
53
  constructor();
45
54
  ngOnInit(): Promise<void>;
46
55
  private buildForm;
47
56
  /** Preguntas ordenadas — el template las recorre en este orden. */
48
57
  readonly questions: import("@angular/core").Signal<import("valtech-components").FieldSchemaDef[]>;
49
58
  questionControl(name: string): FormControl;
59
+ booleanControl(name: string): FormControl<boolean>;
60
+ optionControl(name: string, optionId: string): FormControl<boolean>;
61
+ /** Opciones tal como las escribió quien creó la encuesta — no se traducen. */
62
+ optionsOf(q: {
63
+ options?: {
64
+ id: string;
65
+ name: string;
66
+ order?: number;
67
+ }[];
68
+ }): InputOption[];
69
+ /**
70
+ * `val-radio-input` pide un `InputMetadata` completo (no `Partial`), así que
71
+ * se arma acá en vez de en el template.
72
+ */
73
+ radioProps(q: FieldSchemaDef): InputMetadata;
74
+ placeholderOf(q: {
75
+ placeholderText?: string;
76
+ placeholder?: string;
77
+ }): string;
78
+ hintOf(q: {
79
+ hintText?: string;
80
+ hint?: string;
81
+ }): string;
82
+ /**
83
+ * Tipo del `<input>` para las preguntas que se responden escribiendo. Nada de
84
+ * `parseInputType` de `FormSchemaBuilderService`: ese mapea al enum de
85
+ * `val-form`, y acá no hay `val-form` de por medio (ver el comentario de
86
+ * clase). Un tipo desconocido cae a texto, que siempre se puede responder.
87
+ */
88
+ inputTypeOf(type: string): 'text' | 'email' | 'number' | 'tel';
89
+ /** Las opciones marcadas de una pregunta MULTI_SELECT, en el orden del schema. */
90
+ private selectedOptions;
50
91
  questionLabel(q: {
51
92
  label?: string;
52
93
  labelKey?: string;
@@ -381,6 +381,12 @@ export interface ButtonMetadata {
381
381
  type: 'button' | 'submit' | 'reset';
382
382
  /** Optional token identifier */
383
383
  token?: string;
384
+ /**
385
+ * Nombre accesible del botón. Obligatorio en un botón `icon-only`: sin texto
386
+ * visible, un lector de pantalla solo anuncia "botón" (el `ion-icon` va
387
+ * `aria-hidden`). Ignorado cuando el botón tiene texto propio.
388
+ */
389
+ ariaLabel?: string;
384
390
  /** Optional reference */
385
391
  ref?: any;
386
392
  /** Action handler */
package/lib/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export declare const VERSION = "4.0.982";
5
+ export declare const VERSION = "4.0.983";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valtech-components",
3
- "version": "4.0.982",
3
+ "version": "4.0.983",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "valtech-firebase-config": "./src/lib/services/firebase/scripts/generate-sw-config.js"
package/public-api.d.ts CHANGED
@@ -240,6 +240,8 @@ export * from './lib/components/organisms/access-request-view/access-request-vie
240
240
  export * from './lib/components/organisms/access-request-view/access-request-view.i18n';
241
241
  export * from './lib/components/organisms/survey-response/survey-response.component';
242
242
  export * from './lib/components/organisms/survey-response/types';
243
+ export * from './lib/components/organisms/field-schema-editor/field-schema-editor.component';
244
+ export * from './lib/components/organisms/field-schema-editor/types';
243
245
  export * from './lib/components/organisms/survey-builder/survey-builder.component';
244
246
  export * from './lib/components/organisms/survey-builder/types';
245
247
  export * from './lib/components/molecules/empty-state/empty-state.component';