valtech-components 4.0.1005 → 4.0.1007

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 (21) hide show
  1. package/esm2022/lib/components/organisms/field-schema-editor/field-schema-editor.component.mjs +14 -3
  2. package/esm2022/lib/components/organisms/field-schema-editor/field-schema-editor.i18n.mjs +5 -1
  3. package/esm2022/lib/components/organisms/field-schema-editor/types.mjs +1 -1
  4. package/esm2022/lib/components/organisms/survey-builder/survey-builder.component.mjs +88 -13
  5. package/esm2022/lib/components/organisms/survey-builder/survey-builder.i18n.mjs +15 -1
  6. package/esm2022/lib/components/organisms/survey-builder/survey-preview-modal.component.mjs +129 -0
  7. package/esm2022/lib/components/organisms/survey-response/survey-response.component.mjs +135 -5
  8. package/esm2022/lib/components/organisms/survey-response/survey-response.i18n.mjs +7 -1
  9. package/esm2022/lib/services/forms/form-schema-builder.service.mjs +18 -4
  10. package/esm2022/lib/services/forms/types.mjs +1 -1
  11. package/esm2022/lib/version.mjs +2 -2
  12. package/fesm2022/valtech-components.mjs +396 -21
  13. package/fesm2022/valtech-components.mjs.map +1 -1
  14. package/lib/components/organisms/field-schema-editor/field-schema-editor.component.d.ts +1 -0
  15. package/lib/components/organisms/field-schema-editor/types.d.ts +2 -0
  16. package/lib/components/organisms/survey-builder/survey-builder.component.d.ts +7 -0
  17. package/lib/components/organisms/survey-builder/survey-preview-modal.component.d.ts +28 -0
  18. package/lib/components/organisms/survey-response/survey-response.component.d.ts +11 -1
  19. package/lib/services/forms/types.d.ts +6 -0
  20. package/lib/version.d.ts +1 -1
  21. package/package.json +1 -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.1005';
73
+ const VERSION = '4.0.1007';
74
74
 
75
75
  function evaluateValtechAccess(rule, context, features = {}, visitedFeatures = new Set()) {
76
76
  if (rule == null)
@@ -39116,11 +39116,22 @@ class FormSchemaBuilderService {
39116
39116
  }
39117
39117
  buildForm(schema, i18nNamespace, formName, submitLabelKey = 'submit', state = ComponentStates.ENABLED) {
39118
39118
  const sorted = [...schema].sort((a, b) => a.order - b.order);
39119
- const fields = sorted.map(def => this.buildField(def, i18nNamespace, state));
39120
- const section = { name: '', order: 0, fields };
39119
+ const sectionsById = new Map();
39120
+ sorted.forEach(def => {
39121
+ const key = def.sectionId || def.sectionLabel || '';
39122
+ const section = sectionsById.get(key) ??
39123
+ {
39124
+ name: def.sectionLabel || '',
39125
+ order: def.sectionOrder ?? 0,
39126
+ fields: [],
39127
+ };
39128
+ section.fields.push(this.buildField(def, i18nNamespace, state));
39129
+ sectionsById.set(key, section);
39130
+ });
39131
+ const sections = [...sectionsById.values()].sort((a, b) => a.order - b.order);
39121
39132
  return {
39122
39133
  name: formName,
39123
- sections: [section],
39134
+ sections,
39124
39135
  actions: button({
39125
39136
  text: this.i18n.t(submitLabelKey, i18nNamespace),
39126
39137
  type: 'submit',
@@ -39195,6 +39206,9 @@ class FormSchemaBuilderService {
39195
39206
  required: !!field.required,
39196
39207
  validators,
39197
39208
  options: this.normalizeOptions(field.options),
39209
+ sectionId: field.sectionId,
39210
+ sectionLabel: field.sectionLabel?.trim() ?? '',
39211
+ sectionOrder: field.sectionOrder,
39198
39212
  step: field.step,
39199
39213
  unitLabel: field.unitLabel,
39200
39214
  numberPickerVariant: field.numberPickerVariant,
@@ -39399,6 +39413,9 @@ const SURVEY_RESPONSE_I18N = {
39399
39413
  required: 'Esta pregunta es obligatoria',
39400
39414
  yes: 'Sí',
39401
39415
  no: 'No',
39416
+ previous: 'Volver',
39417
+ next: 'Siguiente',
39418
+ stepsLabel: 'Pasos de la encuesta',
39402
39419
  submit: 'Enviar',
39403
39420
  alreadyTitle: 'Ya respondiste esta encuesta',
39404
39421
  alreadyBody: 'Tu respuesta quedó registrada la primera vez. Gracias por tu tiempo.',
@@ -39422,6 +39439,9 @@ const SURVEY_RESPONSE_I18N = {
39422
39439
  required: 'This question is required',
39423
39440
  yes: 'Yes',
39424
39441
  no: 'No',
39442
+ previous: 'Back',
39443
+ next: 'Next',
39444
+ stepsLabel: 'Survey steps',
39425
39445
  submit: 'Submit',
39426
39446
  alreadyTitle: 'You already answered this survey',
39427
39447
  alreadyBody: 'Your response was recorded the first time. Thanks for your time.',
@@ -39479,6 +39499,8 @@ class SurveyResponseComponent {
39479
39499
  this.sending = signal(false);
39480
39500
  this.typeConfig = signal(null);
39481
39501
  this.alreadyAnswered = signal(false);
39502
+ this.responseStep = signal(0);
39503
+ this.responseValues = signal({});
39482
39504
  this.nameControl = new FormControl('', { nonNullable: true });
39483
39505
  this.emailControl = new FormControl('', { nonNullable: true });
39484
39506
  this.form = new FormGroup({});
@@ -39487,7 +39509,20 @@ class SurveyResponseComponent {
39487
39509
  if (!cfg)
39488
39510
  return null;
39489
39511
  const form = this.formSchemas.buildForm(this.surveySchemaForForm(cfg.fieldSchema), NAMESPACE$4, '', 'submit', this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED);
39490
- return { ...form, showDividers: false, fieldSpacing: 16 };
39512
+ const sections = this.hasResponseSteps() ? [this.sectionWithStoredValues(this.responseSections()[this.responseStep()])] : form.sections;
39513
+ return {
39514
+ ...form,
39515
+ sections,
39516
+ showDividers: false,
39517
+ fieldSpacing: 16,
39518
+ controlled: this.hasResponseSteps(),
39519
+ };
39520
+ });
39521
+ this.responseSections = computed(() => {
39522
+ const cfg = this.typeConfig();
39523
+ if (!cfg)
39524
+ return [];
39525
+ return this.formSchemas.buildForm(this.surveySchemaForForm(cfg.fieldSchema), NAMESPACE$4, '', 'submit').sections;
39491
39526
  });
39492
39527
  /** Preguntas ordenadas, expuestas para tests/hosts que inspeccionan el schema resuelto. */
39493
39528
  this.questions = computed(() => {
@@ -39565,6 +39600,19 @@ class SurveyResponseComponent {
39565
39600
  }
39566
39601
  this.form = new FormGroup(group);
39567
39602
  }
39603
+ hasResponseSteps() {
39604
+ return this.responseSections().filter(section => !!section.name).length > 1;
39605
+ }
39606
+ sectionWithStoredValues(section) {
39607
+ const values = this.responseValues();
39608
+ return {
39609
+ ...section,
39610
+ fields: section.fields.map(field => ({
39611
+ ...field,
39612
+ value: Object.prototype.hasOwnProperty.call(values, field.name) ? values[field.name] : field.value,
39613
+ })),
39614
+ };
39615
+ }
39568
39616
  questionControl(name) {
39569
39617
  return (this.formComponent?.Form.get(name) ?? this.form.get(name));
39570
39618
  }
@@ -39667,10 +39715,14 @@ class SurveyResponseComponent {
39667
39715
  if (!cfg)
39668
39716
  return;
39669
39717
  this.sending.set(true);
39718
+ const fields = {
39719
+ ...this.responseValues(),
39720
+ ...(event?.fields ?? this.formComponent?.Form.getRawValue() ?? this.form.getRawValue()),
39721
+ };
39670
39722
  const payload = {
39671
39723
  type: cfg.typeId,
39672
39724
  title: cfg.label,
39673
- fields: event?.fields ?? this.formComponent?.Form.getRawValue() ?? this.form.getRawValue(),
39725
+ fields,
39674
39726
  ...(this.props.inviteToken ? { inviteToken: this.props.inviteToken } : {}),
39675
39727
  ...(this.needsIdentity()
39676
39728
  ? { submitter: { name: this.nameControl.value.trim(), email: this.emailControl.value.trim() } }
@@ -39701,6 +39753,54 @@ class SurveyResponseComponent {
39701
39753
  });
39702
39754
  }
39703
39755
  }
39756
+ nextResponseStep() {
39757
+ if (!this.formComponent?.validate()) {
39758
+ this.markInvalid();
39759
+ return;
39760
+ }
39761
+ this.responseValues.update(values => ({ ...values, ...this.formComponent?.Form.getRawValue() }));
39762
+ this.responseStep.update(step => Math.min(step + 1, this.responseSections().length - 1));
39763
+ }
39764
+ previousResponseStep() {
39765
+ this.responseValues.update(values => ({ ...values, ...this.formComponent?.Form.getRawValue() }));
39766
+ this.responseStep.update(step => Math.max(step - 1, 0));
39767
+ }
39768
+ responseBackProps() {
39769
+ return {
39770
+ token: 'survey-response-back',
39771
+ text: this.t('previous'),
39772
+ color: 'dark',
39773
+ fill: 'outline',
39774
+ shape: 'round',
39775
+ size: 'default',
39776
+ type: 'button',
39777
+ state: ComponentStates.ENABLED,
39778
+ };
39779
+ }
39780
+ responseNextProps() {
39781
+ return {
39782
+ token: 'survey-response-next',
39783
+ text: this.t('next'),
39784
+ color: 'dark',
39785
+ fill: 'solid',
39786
+ shape: 'round',
39787
+ size: 'default',
39788
+ type: 'button',
39789
+ state: ComponentStates.ENABLED,
39790
+ };
39791
+ }
39792
+ responseSubmitProps() {
39793
+ return {
39794
+ token: 'survey-response-submit',
39795
+ text: this.t('submit'),
39796
+ color: 'dark',
39797
+ fill: 'solid',
39798
+ shape: 'round',
39799
+ size: 'default',
39800
+ type: 'button',
39801
+ state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
39802
+ };
39803
+ }
39704
39804
  hasMissingRequiredMultiSelect() {
39705
39805
  return this.questions()
39706
39806
  .filter(q => q.type === 'MULTI_SELECT' && q.required)
@@ -39764,13 +39864,38 @@ class SurveyResponseComponent {
39764
39864
  }
39765
39865
 
39766
39866
  @if (formProps(); as props) {
39867
+ @if (hasResponseSteps()) {
39868
+ <nav class="survey-response__steps" [attr.aria-label]="t('stepsLabel')">
39869
+ @for (section of responseSections(); track section.name; let i = $index) {
39870
+ <span
39871
+ class="survey-response__step"
39872
+ [class.survey-response__step--active]="responseStep() === i"
39873
+ [attr.aria-current]="responseStep() === i ? 'step' : null"
39874
+ >
39875
+ {{ i + 1 }}
39876
+ </span>
39877
+ }
39878
+ </nav>
39879
+ }
39767
39880
  <val-form [props]="props" (onSubmit)="submit($event)" (onInvalid)="markInvalid()" />
39881
+ @if (hasResponseSteps()) {
39882
+ <div class="survey-response__actions">
39883
+ @if (responseStep() > 0) {
39884
+ <val-button [props]="responseBackProps()" (onClick)="previousResponseStep()" />
39885
+ }
39886
+ @if (responseStep() < responseSections().length - 1) {
39887
+ <val-button [props]="responseNextProps()" (onClick)="nextResponseStep()" />
39888
+ } @else {
39889
+ <val-button [props]="responseSubmitProps()" (onClick)="submit()" />
39890
+ }
39891
+ </div>
39892
+ }
39768
39893
  }
39769
39894
  </div>
39770
39895
  }
39771
39896
  }
39772
39897
  }
39773
- `, isInline: true, styles: [":host{display:block}.survey-response{display:flex;flex-direction:column;gap:16px}.survey-response__header-image,.survey-response__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-response__success{align-items:center;text-align:center}.survey-response__success-image{max-width:320px}.survey-response__success-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.95rem;font-weight:600}.survey-response__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}.survey-response__hint{display:block;margin-top:4px;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.survey-response__loading{min-height:120px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextInputComponent, selector: "val-text-input", inputs: ["preset", "props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onValueChange", "onInvalid", "onSelectChange"] }] }); }
39898
+ `, isInline: true, styles: [":host{display:block}.survey-response{display:flex;flex-direction:column;gap:16px}.survey-response__header-image,.survey-response__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-response__success{align-items:center;text-align:center}.survey-response__success-image{max-width:320px}.survey-response__success-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.95rem;font-weight:600}.survey-response__steps{display:flex;gap:6px}.survey-response__step{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:999px;color:var(--ion-color-medium, #92949c);font-size:.8125rem;font-weight:700}.survey-response__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-response__actions{display:flex;justify-content:flex-end;gap:8px}.survey-response__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}.survey-response__hint{display:block;margin-top:4px;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.survey-response__loading{min-height:120px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextInputComponent, selector: "val-text-input", inputs: ["preset", "props"] }, { 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: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onValueChange", "onInvalid", "onSelectChange"] }] }); }
39774
39899
  }
39775
39900
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyResponseComponent, decorators: [{
39776
39901
  type: Component,
@@ -39841,13 +39966,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
39841
39966
  }
39842
39967
 
39843
39968
  @if (formProps(); as props) {
39969
+ @if (hasResponseSteps()) {
39970
+ <nav class="survey-response__steps" [attr.aria-label]="t('stepsLabel')">
39971
+ @for (section of responseSections(); track section.name; let i = $index) {
39972
+ <span
39973
+ class="survey-response__step"
39974
+ [class.survey-response__step--active]="responseStep() === i"
39975
+ [attr.aria-current]="responseStep() === i ? 'step' : null"
39976
+ >
39977
+ {{ i + 1 }}
39978
+ </span>
39979
+ }
39980
+ </nav>
39981
+ }
39844
39982
  <val-form [props]="props" (onSubmit)="submit($event)" (onInvalid)="markInvalid()" />
39983
+ @if (hasResponseSteps()) {
39984
+ <div class="survey-response__actions">
39985
+ @if (responseStep() > 0) {
39986
+ <val-button [props]="responseBackProps()" (onClick)="previousResponseStep()" />
39987
+ }
39988
+ @if (responseStep() < responseSections().length - 1) {
39989
+ <val-button [props]="responseNextProps()" (onClick)="nextResponseStep()" />
39990
+ } @else {
39991
+ <val-button [props]="responseSubmitProps()" (onClick)="submit()" />
39992
+ }
39993
+ </div>
39994
+ }
39845
39995
  }
39846
39996
  </div>
39847
39997
  }
39848
39998
  }
39849
39999
  }
39850
- `, styles: [":host{display:block}.survey-response{display:flex;flex-direction:column;gap:16px}.survey-response__header-image,.survey-response__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-response__success{align-items:center;text-align:center}.survey-response__success-image{max-width:320px}.survey-response__success-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.95rem;font-weight:600}.survey-response__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}.survey-response__hint{display:block;margin-top:4px;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.survey-response__loading{min-height:120px}\n"] }]
40000
+ `, styles: [":host{display:block}.survey-response{display:flex;flex-direction:column;gap:16px}.survey-response__header-image,.survey-response__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-response__success{align-items:center;text-align:center}.survey-response__success-image{max-width:320px}.survey-response__success-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.95rem;font-weight:600}.survey-response__steps{display:flex;gap:6px}.survey-response__step{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:999px;color:var(--ion-color-medium, #92949c);font-size:.8125rem;font-weight:700}.survey-response__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-response__actions{display:flex;justify-content:flex-end;gap:8px}.survey-response__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}.survey-response__hint{display:block;margin-top:4px;font-size:.8125rem;color:var(--ion-color-medium, #92949c)}.survey-response__loading{min-height:120px}\n"] }]
39851
40001
  }], ctorParameters: () => [], propDecorators: { props: [{
39852
40002
  type: Input
39853
40003
  }], submitted: [{
@@ -39865,6 +40015,8 @@ const FIELD_SCHEMA_EDITOR_I18N = {
39865
40015
  typeLabel: 'Tipo de campo',
39866
40016
  placeholderLabel: 'Texto de ayuda dentro del campo',
39867
40017
  hintLabel: 'Nota bajo el campo',
40018
+ sectionLabel: 'Sección',
40019
+ sectionPlaceholder: 'Ej: Información personal',
39868
40020
  requiredLabel: 'Obligatorio',
39869
40021
  optionsTitle: 'Opciones',
39870
40022
  optionsHint: 'Estas son las alternativas que puede elegir quien responde.',
@@ -39900,6 +40052,8 @@ const FIELD_SCHEMA_EDITOR_I18N = {
39900
40052
  typeLabel: 'Field type',
39901
40053
  placeholderLabel: 'Helper text inside the field',
39902
40054
  hintLabel: 'Note below the field',
40055
+ sectionLabel: 'Section',
40056
+ sectionPlaceholder: 'E.g: Personal information',
39903
40057
  requiredLabel: 'Required',
39904
40058
  optionsTitle: 'Options',
39905
40059
  optionsHint: 'These are the choices the person can pick.',
@@ -39974,6 +40128,7 @@ class FieldSchemaEditorComponent {
39974
40128
  this.labelControl = new FormControl('', { nonNullable: true });
39975
40129
  this.placeholderControl = new FormControl('', { nonNullable: true });
39976
40130
  this.hintControl = new FormControl('', { nonNullable: true });
40131
+ this.sectionControl = new FormControl('', { nonNullable: true });
39977
40132
  this.requiredControl = new FormControl(true, { nonNullable: true });
39978
40133
  this.typeControl = new FormControl('TEXT', { nonNullable: true });
39979
40134
  this.options = signal([]);
@@ -40029,6 +40184,7 @@ class FieldSchemaEditorComponent {
40029
40184
  });
40030
40185
  this.placeholderControl.valueChanges.subscribe(() => this.emitChange());
40031
40186
  this.hintControl.valueChanges.subscribe(() => this.emitChange());
40187
+ this.sectionControl.valueChanges.subscribe(() => this.emitChange());
40032
40188
  this.requiredControl.valueChanges.subscribe(() => this.emitChange());
40033
40189
  }
40034
40190
  ngOnInit() {
@@ -40040,6 +40196,7 @@ class FieldSchemaEditorComponent {
40040
40196
  this.labelControl.setValue(initial.label ?? '');
40041
40197
  this.placeholderControl.setValue(initial.placeholderText ?? '');
40042
40198
  this.hintControl.setValue(initial.hintText ?? '');
40199
+ this.sectionControl.setValue(initial.sectionLabel ?? '');
40043
40200
  this.requiredControl.setValue(!!initial.required);
40044
40201
  this.typeControl.setValue(this.coerceType(initial.type));
40045
40202
  this.options.set(initial.options ?? []);
@@ -40087,6 +40244,7 @@ class FieldSchemaEditorComponent {
40087
40244
  this.labelControl.setValue(value.label ?? '');
40088
40245
  this.placeholderControl.setValue(value.placeholderText ?? '');
40089
40246
  this.hintControl.setValue(value.hintText ?? '');
40247
+ this.sectionControl.setValue(value.sectionLabel ?? '');
40090
40248
  this.requiredControl.setValue(!!value.required);
40091
40249
  this.typeControl.setValue(this.coerceType(value.type));
40092
40250
  this.options.set(value.options ?? []);
@@ -40121,6 +40279,7 @@ class FieldSchemaEditorComponent {
40121
40279
  hintText: (this.hintControl.value || '').trim(),
40122
40280
  type: this.coerceType(this.typeControl.value),
40123
40281
  required: this.requiredControl.value,
40282
+ sectionLabel: (this.sectionControl.value || '').trim(),
40124
40283
  options: this.needsOptions() ? this.options().map((option, index) => ({ ...option, order: index })) : undefined,
40125
40284
  };
40126
40285
  }
@@ -40181,11 +40340,14 @@ class FieldSchemaEditorComponent {
40181
40340
  </val-form-field>
40182
40341
  }
40183
40342
 
40343
+ <val-form-field [label]="t('sectionLabel')">
40344
+ <val-text-input [props]="{ control: sectionControl, placeholder: t('sectionPlaceholder'), state: state() }" />
40345
+ </val-form-field>
40346
+
40184
40347
  <val-toggle-input
40185
40348
  [props]="{
40186
40349
  control: requiredControl,
40187
40350
  label: t('requiredLabel'),
40188
- justify: 'space-between',
40189
40351
  state: state(),
40190
40352
  }"
40191
40353
  />
@@ -40254,11 +40416,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
40254
40416
  </val-form-field>
40255
40417
  }
40256
40418
 
40419
+ <val-form-field [label]="t('sectionLabel')">
40420
+ <val-text-input [props]="{ control: sectionControl, placeholder: t('sectionPlaceholder'), state: state() }" />
40421
+ </val-form-field>
40422
+
40257
40423
  <val-toggle-input
40258
40424
  [props]="{
40259
40425
  control: requiredControl,
40260
40426
  label: t('requiredLabel'),
40261
- justify: 'space-between',
40262
40427
  state: state(),
40263
40428
  }"
40264
40429
  />
@@ -40718,6 +40883,9 @@ const SURVEY_BUILDER_I18N = {
40718
40883
  successTitlePlaceholder: 'Ej: Gracias por responder',
40719
40884
  successMessageLabel: 'Mensaje de agradecimiento',
40720
40885
  successMessagePlaceholder: 'Ej: Tu respuesta quedó registrada',
40886
+ successOptionalNote: 'Opcional. Puedes dejar estos valores por defecto o personalizarlos.',
40887
+ defaultSuccessTitle: 'Gracias por responder',
40888
+ defaultSuccessMessage: 'Tu respuesta quedó registrada. Gracias por ayudarnos a mejorar.',
40721
40889
  successImageLabel: 'Imagen de cierre',
40722
40890
  successImagePlaceholder: 'URL de la imagen que se muestra al terminar',
40723
40891
  questionsLabel: 'Preguntas',
@@ -40745,8 +40913,12 @@ const SURVEY_BUILDER_I18N = {
40745
40913
  wizardStepsLabel: 'Pasos para crear la encuesta',
40746
40914
  next: 'Siguiente',
40747
40915
  previous: 'Volver',
40916
+ preview: 'Vista previa',
40917
+ previewTitle: 'Vista previa',
40918
+ closePreview: 'Cerrar',
40748
40919
  reviewTitle: 'Resumen',
40749
40920
  reviewNoSubtitle: 'Sin subtítulo',
40921
+ reviewQuestionCount: '{count} pregunta',
40750
40922
  reviewQuestionsCount: '{count} preguntas',
40751
40923
  reviewHeaderImage: 'Imagen inicial configurada',
40752
40924
  reviewSuccessImage: 'Imagen de cierre configurada',
@@ -40770,6 +40942,9 @@ const SURVEY_BUILDER_I18N = {
40770
40942
  successTitlePlaceholder: 'E.g: Thanks for answering',
40771
40943
  successMessageLabel: 'Thank-you message',
40772
40944
  successMessagePlaceholder: 'E.g: Your response was recorded',
40945
+ successOptionalNote: 'Optional. You can keep these default values or customize them.',
40946
+ defaultSuccessTitle: 'Thanks for answering',
40947
+ defaultSuccessMessage: 'Your response was recorded. Thanks for helping us improve.',
40773
40948
  successImageLabel: 'Closing image',
40774
40949
  successImagePlaceholder: 'Image URL shown after submit',
40775
40950
  questionsLabel: 'Questions',
@@ -40797,8 +40972,12 @@ const SURVEY_BUILDER_I18N = {
40797
40972
  wizardStepsLabel: 'Survey builder steps',
40798
40973
  next: 'Next',
40799
40974
  previous: 'Back',
40975
+ preview: 'Preview',
40976
+ previewTitle: 'Preview',
40977
+ closePreview: 'Close',
40800
40978
  reviewTitle: 'Summary',
40801
40979
  reviewNoSubtitle: 'No subtitle',
40980
+ reviewQuestionCount: '{count} question',
40802
40981
  reviewQuestionsCount: '{count} questions',
40803
40982
  reviewHeaderImage: 'Opening image set',
40804
40983
  reviewSuccessImage: 'Closing image set',
@@ -40806,6 +40985,129 @@ const SURVEY_BUILDER_I18N = {
40806
40985
  },
40807
40986
  };
40808
40987
 
40988
+ class SurveyPreviewModalComponent {
40989
+ constructor() {
40990
+ this.modalTitle = '';
40991
+ this.closeLabel = '';
40992
+ this.title = '';
40993
+ this.subtitle = '';
40994
+ this.questions = [];
40995
+ this.successTitle = '';
40996
+ this.successMessage = '';
40997
+ this.requiredSuffix = '';
40998
+ }
40999
+ sections() {
41000
+ const map = new Map();
41001
+ [...this.questions]
41002
+ .sort((a, b) => a.order - b.order)
41003
+ .forEach(question => {
41004
+ const key = question.sectionId || question.sectionLabel || '';
41005
+ const section = map.get(key) ??
41006
+ {
41007
+ name: question.sectionLabel || '',
41008
+ order: question.sectionOrder ?? 0,
41009
+ questions: [],
41010
+ };
41011
+ section.questions.push(question);
41012
+ map.set(key, section);
41013
+ });
41014
+ return [...map.values()].sort((a, b) => a.order - b.order);
41015
+ }
41016
+ close() {
41017
+ this._modalRef?.dismiss(undefined, 'close');
41018
+ }
41019
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyPreviewModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
41020
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SurveyPreviewModalComponent, isStandalone: true, selector: "val-survey-preview-modal", inputs: { modalTitle: "modalTitle", closeLabel: "closeLabel", title: "title", subtitle: "subtitle", presentation: "presentation", questions: "questions", successTitle: "successTitle", successMessage: "successMessage", requiredSuffix: "requiredSuffix", _modalRef: "_modalRef" }, ngImport: i0, template: `
41021
+ <val-modal-layout [title]="modalTitle" [closeLabel]="closeLabel" (close)="close()">
41022
+ <div class="survey-preview">
41023
+ @if (presentation?.headerImageUrl) {
41024
+ <img class="survey-preview__image" [src]="presentation?.headerImageUrl" [alt]="title" />
41025
+ }
41026
+ <val-display [props]="{ content: title, size: 'small', color: 'dark' }" />
41027
+ @if (subtitle) {
41028
+ <val-title [props]="{ content: subtitle, size: 'large', color: '', bold: false }" />
41029
+ }
41030
+ @for (section of sections(); track section.name) {
41031
+ <section class="survey-preview__section">
41032
+ @if (section.name) {
41033
+ <strong class="survey-preview__section-title">{{ section.name }}</strong>
41034
+ }
41035
+ @for (question of section.questions; track question.name) {
41036
+ <article class="survey-preview__question">
41037
+ <strong>{{ question.label }}</strong>
41038
+ <span>{{ question.typeLabel }}{{ question.required ? requiredSuffix : '' }}</span>
41039
+ </article>
41040
+ }
41041
+ </section>
41042
+ }
41043
+ <section class="survey-preview__success">
41044
+ @if (presentation?.successImageUrl) {
41045
+ <img class="survey-preview__success-image" [src]="presentation?.successImageUrl" [alt]="successTitle" />
41046
+ }
41047
+ <strong>{{ successTitle }}</strong>
41048
+ <span>{{ successMessage }}</span>
41049
+ </section>
41050
+ </div>
41051
+ </val-modal-layout>
41052
+ `, isInline: true, styles: [":host{display:block}.survey-preview{display:flex;flex-direction:column;gap:16px}.survey-preview__image,.survey-preview__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-preview__section{display:flex;flex-direction:column;gap:10px}.survey-preview__section-title{color:var(--ion-text-color, #000);font-size:1rem}.survey-preview__question,.survey-preview__success{display:flex;flex-direction:column;gap:4px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-preview__question span,.survey-preview__success span{color:var(--ion-color-medium, #92949c);font-size:.875rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ModalLayoutComponent, selector: "val-modal-layout", inputs: ["title", "subtitle", "closeLabel", "showClose", "actions", "actionsAlign", "footer", "footerClass"], outputs: ["close", "actionClick"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
41053
+ }
41054
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyPreviewModalComponent, decorators: [{
41055
+ type: Component,
41056
+ args: [{ selector: 'val-survey-preview-modal', standalone: true, imports: [CommonModule, ModalLayoutComponent, DisplayComponent, TitleComponent], template: `
41057
+ <val-modal-layout [title]="modalTitle" [closeLabel]="closeLabel" (close)="close()">
41058
+ <div class="survey-preview">
41059
+ @if (presentation?.headerImageUrl) {
41060
+ <img class="survey-preview__image" [src]="presentation?.headerImageUrl" [alt]="title" />
41061
+ }
41062
+ <val-display [props]="{ content: title, size: 'small', color: 'dark' }" />
41063
+ @if (subtitle) {
41064
+ <val-title [props]="{ content: subtitle, size: 'large', color: '', bold: false }" />
41065
+ }
41066
+ @for (section of sections(); track section.name) {
41067
+ <section class="survey-preview__section">
41068
+ @if (section.name) {
41069
+ <strong class="survey-preview__section-title">{{ section.name }}</strong>
41070
+ }
41071
+ @for (question of section.questions; track question.name) {
41072
+ <article class="survey-preview__question">
41073
+ <strong>{{ question.label }}</strong>
41074
+ <span>{{ question.typeLabel }}{{ question.required ? requiredSuffix : '' }}</span>
41075
+ </article>
41076
+ }
41077
+ </section>
41078
+ }
41079
+ <section class="survey-preview__success">
41080
+ @if (presentation?.successImageUrl) {
41081
+ <img class="survey-preview__success-image" [src]="presentation?.successImageUrl" [alt]="successTitle" />
41082
+ }
41083
+ <strong>{{ successTitle }}</strong>
41084
+ <span>{{ successMessage }}</span>
41085
+ </section>
41086
+ </div>
41087
+ </val-modal-layout>
41088
+ `, styles: [":host{display:block}.survey-preview{display:flex;flex-direction:column;gap:16px}.survey-preview__image,.survey-preview__success-image{display:block;width:100%;max-height:220px;object-fit:contain;border-radius:var(--val-radius-md, 12px)}.survey-preview__section{display:flex;flex-direction:column;gap:10px}.survey-preview__section-title{color:var(--ion-text-color, #000);font-size:1rem}.survey-preview__question,.survey-preview__success{display:flex;flex-direction:column;gap:4px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-preview__question span,.survey-preview__success span{color:var(--ion-color-medium, #92949c);font-size:.875rem}\n"] }]
41089
+ }], propDecorators: { modalTitle: [{
41090
+ type: Input
41091
+ }], closeLabel: [{
41092
+ type: Input
41093
+ }], title: [{
41094
+ type: Input
41095
+ }], subtitle: [{
41096
+ type: Input
41097
+ }], presentation: [{
41098
+ type: Input
41099
+ }], questions: [{
41100
+ type: Input
41101
+ }], successTitle: [{
41102
+ type: Input
41103
+ }], successMessage: [{
41104
+ type: Input
41105
+ }], requiredSuffix: [{
41106
+ type: Input
41107
+ }], _modalRef: [{
41108
+ type: Input
41109
+ }] } });
41110
+
40809
41111
  const NAMESPACE$1 = 'SurveyBuilder';
40810
41112
  const EDITOR_NAMESPACE = 'FieldSchemaEditor';
40811
41113
  const DEFAULT_TYPE_PREFIX = 'survey';
@@ -40842,6 +41144,7 @@ class SurveyBuilderComponent {
40842
41144
  this.requests = inject(RequestService);
40843
41145
  this.formBuilder = inject(RequestFormBuilderService);
40844
41146
  this.errors = inject(ValtechErrorService);
41147
+ this.modals = inject(ModalService);
40845
41148
  this.titleControl = new FormControl('', { nonNullable: true });
40846
41149
  this.subtitleControl = new FormControl('', { nonNullable: true });
40847
41150
  this.headerImageControl = new FormControl('', { nonNullable: true });
@@ -40871,7 +41174,9 @@ class SurveyBuilderComponent {
40871
41174
  if (!this.i18n.hasNamespace(EDITOR_NAMESPACE)) {
40872
41175
  this.i18n.registerDefaults(EDITOR_NAMESPACE, FIELD_SCHEMA_EDITOR_I18N);
40873
41176
  }
40874
- addIcons({ cloudUploadOutline, trashOutline });
41177
+ this.successTitleControl.setValue(this.defaultSuccessTitle());
41178
+ this.successMessageControl.setValue(this.defaultSuccessMessage());
41179
+ addIcons({ cloudUploadOutline, eyeOutline, trashOutline });
40875
41180
  }
40876
41181
  async ngOnInit() {
40877
41182
  // Sembrado directo: la app ya tenía la encuesta, no hace falta leerla.
@@ -41120,6 +41425,9 @@ class SurveyBuilderComponent {
41120
41425
  label: row.label.trim(),
41121
41426
  type: row.type,
41122
41427
  required: row.required,
41428
+ sectionId: row.sectionLabel ? this.formBuilder.toToken(row.sectionLabel) : undefined,
41429
+ sectionLabel: row.sectionLabel,
41430
+ sectionOrder: this.sectionOrderFor(row.sectionLabel, rows),
41123
41431
  placeholderText: row.placeholderText,
41124
41432
  hintText: row.hintText,
41125
41433
  options: row.options,
@@ -41171,20 +41479,26 @@ class SurveyBuilderComponent {
41171
41479
  this.titleControl.setValue(label ?? '');
41172
41480
  this.subtitleControl.setValue(subtitle ?? '');
41173
41481
  this.headerImageControl.setValue(presentation?.headerImageUrl ?? '');
41174
- this.successTitleControl.setValue(presentation?.successTitle ?? '');
41175
- this.successMessageControl.setValue(presentation?.successMessage ?? '');
41482
+ this.successTitleControl.setValue(presentation?.successTitle || this.defaultSuccessTitle());
41483
+ this.successMessageControl.setValue(presentation?.successMessage || this.defaultSuccessMessage());
41176
41484
  this.successImageControl.setValue(presentation?.successImageUrl ?? '');
41177
41485
  this.questions.set([...(questions ?? [])].sort((a, b) => a.order - b.order).map(f => this.rowFromField(f)));
41178
41486
  }
41179
41487
  presentationPayload() {
41180
41488
  const presentation = {
41181
41489
  headerImageUrl: this.headerImageControl.value.trim(),
41182
- successTitle: this.successTitleControl.value.trim(),
41183
- successMessage: this.successMessageControl.value.trim(),
41490
+ successTitle: this.successTitleControl.value.trim() || this.defaultSuccessTitle(),
41491
+ successMessage: this.successMessageControl.value.trim() || this.defaultSuccessMessage(),
41184
41492
  successImageUrl: this.successImageControl.value.trim(),
41185
41493
  };
41186
41494
  return Object.values(presentation).some(Boolean) ? presentation : undefined;
41187
41495
  }
41496
+ defaultSuccessTitle() {
41497
+ return this.t('defaultSuccessTitle');
41498
+ }
41499
+ defaultSuccessMessage() {
41500
+ return this.t('defaultSuccessMessage');
41501
+ }
41188
41502
  validateWizardStep(step) {
41189
41503
  this.validationError.set('');
41190
41504
  if (step === 0) {
@@ -41223,9 +41537,60 @@ class SurveyBuilderComponent {
41223
41537
  hintText: f.hintText ?? '',
41224
41538
  type: f.type,
41225
41539
  required: !!f.required,
41540
+ sectionLabel: f.sectionLabel ?? '',
41541
+ sectionOrder: f.sectionOrder,
41226
41542
  options: f.options,
41227
41543
  };
41228
41544
  }
41545
+ reviewQuestionsCount() {
41546
+ const count = this.questions().length;
41547
+ const key = count === 1 ? 'reviewQuestionCount' : 'reviewQuestionsCount';
41548
+ return this.interpolate(this.t(key), { count });
41549
+ }
41550
+ previewProps() {
41551
+ return {
41552
+ token: 'survey-builder-preview',
41553
+ text: this.t('preview'),
41554
+ color: 'dark',
41555
+ fill: 'outline',
41556
+ shape: 'round',
41557
+ size: 'default',
41558
+ type: 'button',
41559
+ state: ComponentStates.ENABLED,
41560
+ icon: { name: 'eye-outline', slot: 'start' },
41561
+ };
41562
+ }
41563
+ async openPreview() {
41564
+ await this.modals.open({
41565
+ component: SurveyPreviewModalComponent,
41566
+ componentProps: {
41567
+ modalTitle: this.t('previewTitle'),
41568
+ closeLabel: this.t('closePreview'),
41569
+ title: this.titleControl.value.trim() || this.t('untitledQuestion'),
41570
+ subtitle: this.subtitleControl.value.trim(),
41571
+ presentation: this.presentationPayload(),
41572
+ successTitle: this.successTitleControl.value.trim() || this.defaultSuccessTitle(),
41573
+ successMessage: this.successMessageControl.value.trim() || this.defaultSuccessMessage(),
41574
+ requiredSuffix: ` · ${this.t('requiredTag')}`,
41575
+ questions: this.questions().map((question, index) => ({
41576
+ ...question,
41577
+ order: index,
41578
+ name: question.name || this.formBuilder.toToken(question.label),
41579
+ typeLabel: this.typeLabel(question.type),
41580
+ sectionId: question.sectionLabel ? this.formBuilder.toToken(question.sectionLabel) : undefined,
41581
+ sectionOrder: this.sectionOrderFor(question.sectionLabel, this.questions()),
41582
+ })),
41583
+ },
41584
+ size: 'medium',
41585
+ });
41586
+ }
41587
+ sectionOrderFor(sectionLabel, rows) {
41588
+ const label = sectionLabel?.trim();
41589
+ if (!label)
41590
+ return undefined;
41591
+ const labels = [...new Set(rows.map(row => row.sectionLabel?.trim()).filter(Boolean))];
41592
+ return labels.indexOf(label);
41593
+ }
41229
41594
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
41230
41595
  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: `
41231
41596
  <div class="survey-builder">
@@ -41338,6 +41703,7 @@ class SurveyBuilderComponent {
41338
41703
  </label>
41339
41704
  }
41340
41705
  </div>
41706
+ <p class="survey-builder__optional-note">{{ t('successOptionalNote') }}</p>
41341
41707
  <val-form-field [label]="t('successTitleLabel')">
41342
41708
  <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41343
41709
  </val-form-field>
@@ -41384,6 +41750,7 @@ class SurveyBuilderComponent {
41384
41750
  @if (showWizardSection(2)) {
41385
41751
  <section class="survey-builder__section">
41386
41752
  <span class="survey-builder__questions-label">{{ t('stepClose') }}</span>
41753
+ <p class="survey-builder__optional-note">{{ t('successOptionalNote') }}</p>
41387
41754
  <val-form-field [label]="t('successTitleLabel')">
41388
41755
  <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41389
41756
  </val-form-field>
@@ -41440,7 +41807,9 @@ class SurveyBuilderComponent {
41440
41807
  <div class="survey-builder__row-head">
41441
41808
  <div class="survey-builder__row-text">
41442
41809
  <strong>{{ row.label || t('untitledQuestion') }}</strong>
41443
- <span>{{ typeLabel(row.type) }}{{ row.required ? ' · ' + t('requiredTag') : '' }}</span>
41810
+ <span>
41811
+ {{ typeLabel(row.type) }}{{ row.required ? ' · ' + t('requiredTag') : '' }}{{ row.sectionLabel ? ' · ' + row.sectionLabel : '' }}
41812
+ </span>
41444
41813
  </div>
41445
41814
  <div class="survey-builder__row-actions">
41446
41815
  <val-button
@@ -41514,11 +41883,12 @@ class SurveyBuilderComponent {
41514
41883
  <span class="survey-builder__questions-label">{{ t('reviewTitle') }}</span>
41515
41884
  <strong>{{ titleControl.value || t('untitledQuestion') }}</strong>
41516
41885
  <span>{{ subtitleControl.value || t('reviewNoSubtitle') }}</span>
41517
- <span>{{ interpolate(t('reviewQuestionsCount'), { count: questions().length }) }}</span>
41886
+ <span>{{ reviewQuestionsCount() }}</span>
41518
41887
  @if (headerImageControl.value) {
41519
41888
  <span>{{ t('reviewHeaderImage') }}</span>
41520
41889
  }
41521
41890
  <span>{{ successImageControl.value ? t('reviewSuccessImage') : t('reviewDefaultClose') }}</span>
41891
+ <val-button [props]="previewProps()" (onClick)="openPreview()" />
41522
41892
  </section>
41523
41893
  }
41524
41894
 
@@ -41541,7 +41911,7 @@ class SurveyBuilderComponent {
41541
41911
  <val-button [props]="saveProps()" (onClick)="save()" />
41542
41912
  }
41543
41913
  </div>
41544
- `, isInline: true, styles: [":host{display:block}.survey-builder{display:flex;flex-direction:column;gap:16px}.survey-builder__questions{display:flex;flex-direction:column;gap:12px}.survey-builder__presentation{display:flex;flex-direction:column;gap:12px;padding-top:4px}.survey-builder__section,.survey-builder__review{display:flex;flex-direction:column;gap:12px}.survey-builder__steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:12px}.survey-builder__step{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:8px;background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium-shade, #6b6675);font-size:.8125rem;font-weight:700;cursor:pointer}.survey-builder__step span{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:999px;background:var(--ion-color-light, #f4f5f8);color:inherit;font-size:.75rem}.survey-builder__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__wizard-actions{display:flex;justify-content:flex-end;gap:8px}.survey-builder__review{padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__review strong{color:var(--ion-text-color, #000)}.survey-builder__review span:not(.survey-builder__questions-label){color:var(--ion-color-medium, #92949c);font-size:.875rem}.survey-builder__image-field{display:flex;flex-direction:column;gap:10px}.survey-builder__image-preview{position:relative;width:100%}.survey-builder__image-preview img{display:block;width:100%;aspect-ratio:16 / 9;object-fit:contain;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;background:var(--ion-color-light, #f4f5f8)}.survey-builder__image-clear{position:absolute;top:8px;right:8px;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:0;border-radius:999px;background:#0009;color:#fff;cursor:pointer}.survey-builder__image-upload{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 18px;border:2px dashed var(--ion-color-dark, #2f2c3a);border-radius:20px;color:var(--ion-color-dark, #2f2c3a);font-size:.875rem;font-weight:600;cursor:pointer}.survey-builder__image-upload:hover{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__image-upload--loading{opacity:.7;cursor:default}.survey-builder__image-upload ion-spinner{width:16px;height:16px}.survey-builder__image-upload input{display:none}.survey-builder__questions-label{font-size:.8125rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ion-color-medium, #92949c)}.survey-builder__empty{margin:0;font-size:.875rem;color:var(--ion-color-medium, #92949c)}.survey-builder__row{display:flex;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__row-head{display:flex;flex-direction:column;gap:8px}.survey-builder__row-text{display:flex;flex-direction:column;gap:2px;min-width:0}.survey-builder__row-text strong{color:var(--ion-text-color, #000);font-size:.9375rem}.survey-builder__row-text span{color:var(--ion-color-medium, #92949c);font-size:.8125rem}.survey-builder__row-actions{display:flex;gap:4px;flex-wrap:wrap}.survey-builder__row-editor{display:flex;flex-direction:column;gap:12px;padding-top:12px;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.survey-builder__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}@media (min-width: 576px){.survey-builder__row-head{flex-direction:row;align-items:flex-start;justify-content:space-between}.survey-builder__row-text{flex:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: TextInputComponent, selector: "val-text-input", inputs: ["preset", "props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: FormFieldComponent, selector: "val-form-field", inputs: ["label"] }, { kind: "component", type: FieldSchemaEditorComponent, selector: "val-field-schema-editor", inputs: ["props"], outputs: ["save", "changed"] }] }); }
41914
+ `, isInline: true, styles: [":host{display:block}.survey-builder{display:flex;flex-direction:column;gap:16px}.survey-builder__questions{display:flex;flex-direction:column;gap:12px}.survey-builder__presentation{display:flex;flex-direction:column;gap:12px;padding-top:4px}.survey-builder__section,.survey-builder__review{display:flex;flex-direction:column;gap:12px}.survey-builder__steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:12px}.survey-builder__step{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:8px;background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium-shade, #6b6675);font-size:.8125rem;font-weight:700;cursor:pointer}.survey-builder__step span{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:999px;background:var(--ion-color-light, #f4f5f8);color:inherit;font-size:.75rem}.survey-builder__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__wizard-actions{display:flex;justify-content:flex-end;gap:8px}.survey-builder__review{padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__review strong{color:var(--ion-text-color, #000)}.survey-builder__review span:not(.survey-builder__questions-label){color:var(--ion-color-medium, #92949c);font-size:.875rem}.survey-builder__image-field{display:flex;flex-direction:column;gap:10px}.survey-builder__image-preview{position:relative;width:100%}.survey-builder__image-preview img{display:block;width:100%;aspect-ratio:16 / 9;object-fit:contain;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;background:var(--ion-color-light, #f4f5f8)}.survey-builder__image-clear{position:absolute;top:8px;right:8px;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:0;border-radius:999px;background:#0009;color:#fff;cursor:pointer}.survey-builder__image-upload{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 18px;border:2px dashed var(--ion-color-dark, #2f2c3a);border-radius:20px;color:var(--ion-color-dark, #2f2c3a);font-size:.875rem;font-weight:600;cursor:pointer}.survey-builder__image-upload:hover{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__image-upload--loading{opacity:.7;cursor:default}.survey-builder__image-upload ion-spinner{width:16px;height:16px}.survey-builder__image-upload input{display:none}.survey-builder__questions-label{font-size:.8125rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ion-color-medium, #92949c)}.survey-builder__empty{margin:0;font-size:.875rem;color:var(--ion-color-medium, #92949c)}.survey-builder__optional-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.875rem;line-height:1.35}.survey-builder__row{display:flex;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__row-head{display:flex;flex-direction:column;gap:8px}.survey-builder__row-text{display:flex;flex-direction:column;gap:2px;min-width:0}.survey-builder__row-text strong{color:var(--ion-text-color, #000);font-size:.9375rem}.survey-builder__row-text span{color:var(--ion-color-medium, #92949c);font-size:.8125rem}.survey-builder__row-actions{display:flex;gap:4px;flex-wrap:wrap}.survey-builder__row-editor{display:flex;flex-direction:column;gap:12px;padding-top:12px;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.survey-builder__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}@media (min-width: 576px){.survey-builder__row-head{flex-direction:row;align-items:flex-start;justify-content:space-between}.survey-builder__row-text{flex:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: TextInputComponent, selector: "val-text-input", inputs: ["preset", "props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: FormFieldComponent, selector: "val-form-field", inputs: ["label"] }, { kind: "component", type: FieldSchemaEditorComponent, selector: "val-field-schema-editor", inputs: ["props"], outputs: ["save", "changed"] }] }); }
41545
41915
  }
41546
41916
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyBuilderComponent, decorators: [{
41547
41917
  type: Component,
@@ -41665,6 +42035,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41665
42035
  </label>
41666
42036
  }
41667
42037
  </div>
42038
+ <p class="survey-builder__optional-note">{{ t('successOptionalNote') }}</p>
41668
42039
  <val-form-field [label]="t('successTitleLabel')">
41669
42040
  <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41670
42041
  </val-form-field>
@@ -41711,6 +42082,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41711
42082
  @if (showWizardSection(2)) {
41712
42083
  <section class="survey-builder__section">
41713
42084
  <span class="survey-builder__questions-label">{{ t('stepClose') }}</span>
42085
+ <p class="survey-builder__optional-note">{{ t('successOptionalNote') }}</p>
41714
42086
  <val-form-field [label]="t('successTitleLabel')">
41715
42087
  <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41716
42088
  </val-form-field>
@@ -41767,7 +42139,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41767
42139
  <div class="survey-builder__row-head">
41768
42140
  <div class="survey-builder__row-text">
41769
42141
  <strong>{{ row.label || t('untitledQuestion') }}</strong>
41770
- <span>{{ typeLabel(row.type) }}{{ row.required ? ' · ' + t('requiredTag') : '' }}</span>
42142
+ <span>
42143
+ {{ typeLabel(row.type) }}{{ row.required ? ' · ' + t('requiredTag') : '' }}{{ row.sectionLabel ? ' · ' + row.sectionLabel : '' }}
42144
+ </span>
41771
42145
  </div>
41772
42146
  <div class="survey-builder__row-actions">
41773
42147
  <val-button
@@ -41841,11 +42215,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41841
42215
  <span class="survey-builder__questions-label">{{ t('reviewTitle') }}</span>
41842
42216
  <strong>{{ titleControl.value || t('untitledQuestion') }}</strong>
41843
42217
  <span>{{ subtitleControl.value || t('reviewNoSubtitle') }}</span>
41844
- <span>{{ interpolate(t('reviewQuestionsCount'), { count: questions().length }) }}</span>
42218
+ <span>{{ reviewQuestionsCount() }}</span>
41845
42219
  @if (headerImageControl.value) {
41846
42220
  <span>{{ t('reviewHeaderImage') }}</span>
41847
42221
  }
41848
42222
  <span>{{ successImageControl.value ? t('reviewSuccessImage') : t('reviewDefaultClose') }}</span>
42223
+ <val-button [props]="previewProps()" (onClick)="openPreview()" />
41849
42224
  </section>
41850
42225
  }
41851
42226
 
@@ -41868,7 +42243,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41868
42243
  <val-button [props]="saveProps()" (onClick)="save()" />
41869
42244
  }
41870
42245
  </div>
41871
- `, styles: [":host{display:block}.survey-builder{display:flex;flex-direction:column;gap:16px}.survey-builder__questions{display:flex;flex-direction:column;gap:12px}.survey-builder__presentation{display:flex;flex-direction:column;gap:12px;padding-top:4px}.survey-builder__section,.survey-builder__review{display:flex;flex-direction:column;gap:12px}.survey-builder__steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:12px}.survey-builder__step{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:8px;background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium-shade, #6b6675);font-size:.8125rem;font-weight:700;cursor:pointer}.survey-builder__step span{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:999px;background:var(--ion-color-light, #f4f5f8);color:inherit;font-size:.75rem}.survey-builder__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__wizard-actions{display:flex;justify-content:flex-end;gap:8px}.survey-builder__review{padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__review strong{color:var(--ion-text-color, #000)}.survey-builder__review span:not(.survey-builder__questions-label){color:var(--ion-color-medium, #92949c);font-size:.875rem}.survey-builder__image-field{display:flex;flex-direction:column;gap:10px}.survey-builder__image-preview{position:relative;width:100%}.survey-builder__image-preview img{display:block;width:100%;aspect-ratio:16 / 9;object-fit:contain;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;background:var(--ion-color-light, #f4f5f8)}.survey-builder__image-clear{position:absolute;top:8px;right:8px;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:0;border-radius:999px;background:#0009;color:#fff;cursor:pointer}.survey-builder__image-upload{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 18px;border:2px dashed var(--ion-color-dark, #2f2c3a);border-radius:20px;color:var(--ion-color-dark, #2f2c3a);font-size:.875rem;font-weight:600;cursor:pointer}.survey-builder__image-upload:hover{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__image-upload--loading{opacity:.7;cursor:default}.survey-builder__image-upload ion-spinner{width:16px;height:16px}.survey-builder__image-upload input{display:none}.survey-builder__questions-label{font-size:.8125rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ion-color-medium, #92949c)}.survey-builder__empty{margin:0;font-size:.875rem;color:var(--ion-color-medium, #92949c)}.survey-builder__row{display:flex;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__row-head{display:flex;flex-direction:column;gap:8px}.survey-builder__row-text{display:flex;flex-direction:column;gap:2px;min-width:0}.survey-builder__row-text strong{color:var(--ion-text-color, #000);font-size:.9375rem}.survey-builder__row-text span{color:var(--ion-color-medium, #92949c);font-size:.8125rem}.survey-builder__row-actions{display:flex;gap:4px;flex-wrap:wrap}.survey-builder__row-editor{display:flex;flex-direction:column;gap:12px;padding-top:12px;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.survey-builder__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}@media (min-width: 576px){.survey-builder__row-head{flex-direction:row;align-items:flex-start;justify-content:space-between}.survey-builder__row-text{flex:1}}\n"] }]
42246
+ `, styles: [":host{display:block}.survey-builder{display:flex;flex-direction:column;gap:16px}.survey-builder__questions{display:flex;flex-direction:column;gap:12px}.survey-builder__presentation{display:flex;flex-direction:column;gap:12px;padding-top:4px}.survey-builder__section,.survey-builder__review{display:flex;flex-direction:column;gap:12px}.survey-builder__steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:12px}.survey-builder__step{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .12));border-radius:8px;background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium-shade, #6b6675);font-size:.8125rem;font-weight:700;cursor:pointer}.survey-builder__step span{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:999px;background:var(--ion-color-light, #f4f5f8);color:inherit;font-size:.75rem}.survey-builder__step--active{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__wizard-actions{display:flex;justify-content:flex-end;gap:8px}.survey-builder__review{padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__review strong{color:var(--ion-text-color, #000)}.survey-builder__review span:not(.survey-builder__questions-label){color:var(--ion-color-medium, #92949c);font-size:.875rem}.survey-builder__image-field{display:flex;flex-direction:column;gap:10px}.survey-builder__image-preview{position:relative;width:100%}.survey-builder__image-preview img{display:block;width:100%;aspect-ratio:16 / 9;object-fit:contain;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:8px;background:var(--ion-color-light, #f4f5f8)}.survey-builder__image-clear{position:absolute;top:8px;right:8px;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:0;border-radius:999px;background:#0009;color:#fff;cursor:pointer}.survey-builder__image-upload{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 18px;border:2px dashed var(--ion-color-dark, #2f2c3a);border-radius:20px;color:var(--ion-color-dark, #2f2c3a);font-size:.875rem;font-weight:600;cursor:pointer}.survey-builder__image-upload:hover{border-color:var(--ion-color-primary);color:var(--ion-color-primary)}.survey-builder__image-upload--loading{opacity:.7;cursor:default}.survey-builder__image-upload ion-spinner{width:16px;height:16px}.survey-builder__image-upload input{display:none}.survey-builder__questions-label{font-size:.8125rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ion-color-medium, #92949c)}.survey-builder__empty{margin:0;font-size:.875rem;color:var(--ion-color-medium, #92949c)}.survey-builder__optional-note{margin:0;color:var(--ion-color-medium, #92949c);font-size:.875rem;line-height:1.35}.survey-builder__row{display:flex;flex-direction:column;gap:12px;padding:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:var(--val-radius-md, 12px);background:var(--ion-card-background, var(--ion-background-color, #fff))}.survey-builder__row-head{display:flex;flex-direction:column;gap:8px}.survey-builder__row-text{display:flex;flex-direction:column;gap:2px;min-width:0}.survey-builder__row-text strong{color:var(--ion-text-color, #000);font-size:.9375rem}.survey-builder__row-text span{color:var(--ion-color-medium, #92949c);font-size:.8125rem}.survey-builder__row-actions{display:flex;gap:4px;flex-wrap:wrap}.survey-builder__row-editor{display:flex;flex-direction:column;gap:12px;padding-top:12px;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .1))}.survey-builder__error{margin:0;color:var(--ion-color-danger);font-size:.875rem;font-weight:700}@media (min-width: 576px){.survey-builder__row-head{flex-direction:row;align-items:flex-start;justify-content:space-between}.survey-builder__row-text{flex:1}}\n"] }]
41872
42247
  }], ctorParameters: () => [], propDecorators: { props: [{
41873
42248
  type: Input
41874
42249
  }], saved: [{