valtech-components 4.0.1001 → 4.0.1003

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.
@@ -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.1001';
73
+ const VERSION = '4.0.1003';
74
74
 
75
75
  function evaluateValtechAccess(rule, context, features = {}, visitedFeatures = new Set()) {
76
76
  if (rule == null)
@@ -39403,7 +39403,8 @@ const SURVEY_RESPONSE_I18N = {
39403
39403
  alreadyTitle: 'Ya respondiste esta encuesta',
39404
39404
  alreadyBody: 'Tu respuesta quedó registrada la primera vez. Gracias por tu tiempo.',
39405
39405
  sentTitle: 'Gracias por responder',
39406
- sentBody: 'Tu respuesta quedó registrada.',
39406
+ sentBody: 'Tu respuesta quedó registrada. Gracias por ayudarnos a mejorar.',
39407
+ sentCloseNote: 'Ya puedes cerrar esta pestaña.',
39407
39408
  loadErrorTitle: 'No pudimos cargar la encuesta',
39408
39409
  loadErrorBody: 'Puede que el link esté vencido o ya no esté disponible.',
39409
39410
  loginRequiredTitle: 'Necesitas iniciar sesión',
@@ -39425,7 +39426,8 @@ const SURVEY_RESPONSE_I18N = {
39425
39426
  alreadyTitle: 'You already answered this survey',
39426
39427
  alreadyBody: 'Your response was recorded the first time. Thanks for your time.',
39427
39428
  sentTitle: 'Thanks for answering',
39428
- sentBody: 'Your response was recorded.',
39429
+ sentBody: 'Your response was recorded. Thanks for helping us improve.',
39430
+ sentCloseNote: 'You can close this tab now.',
39429
39431
  loadErrorTitle: "We couldn't load the survey",
39430
39432
  loadErrorBody: 'The link may be expired or no longer available.',
39431
39433
  loginRequiredTitle: 'You need to sign in',
@@ -39439,6 +39441,7 @@ const NAMESPACE$4 = 'SurveyResponse';
39439
39441
  const INVITE_USED_CODE = 'SURVEY_INVITE_ALREADY_USED';
39440
39442
  /** Tipos cuya respuesta es un booleano, no un texto. */
39441
39443
  const BOOLEAN_TYPES = ['CHECK', 'TOGGLE'];
39444
+ const DEFAULT_SUCCESS_IMAGE = 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 320 220%22 role=%22img%22 aria-label=%22Success%22%3E%3Crect width=%22320%22 height=%22220%22 rx=%2232%22 fill=%22%23fff5fb%22/%3E%3Ccircle cx=%22160%22 cy=%2294%22 r=%2256%22 fill=%22%23f900b2%22/%3E%3Cpath d=%22M132 94.5 153.5 116 191 77%22 fill=%22none%22 stroke=%22%23fff%22 stroke-width=%2214%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22/%3E%3Cpath d=%22M92 168h136%22 stroke=%22%232b2836%22 stroke-width=%2210%22 stroke-linecap=%22round%22 opacity=%22.16%22/%3E%3Cpath d=%22M116 187h88%22 stroke=%22%232b2836%22 stroke-width=%228%22 stroke-linecap=%22round%22 opacity=%22.12%22/%3E%3C/svg%3E';
39442
39445
  /**
39443
39446
  * val-survey-response
39444
39447
  *
@@ -39510,14 +39513,10 @@ class SurveyResponseComponent {
39510
39513
  });
39511
39514
  this.successPresentation = computed(() => {
39512
39515
  const cfg = this.typeConfig();
39513
- if (!cfg?.presentation)
39514
- return null;
39515
- const title = cfg.presentation.successTitle?.trim() || this.t('sentTitle');
39516
- const message = cfg.presentation.successMessage?.trim() || this.t('sentBody');
39517
- const imageUrl = cfg.presentation.successImageUrl?.trim() || '';
39518
- if (!cfg.presentation.successTitle && !cfg.presentation.successMessage && !imageUrl)
39519
- return null;
39520
- return { title, message, imageUrl };
39516
+ const title = cfg?.presentation?.successTitle?.trim() || this.t('sentTitle');
39517
+ const message = cfg?.presentation?.successMessage?.trim() || this.t('sentBody');
39518
+ const imageUrl = cfg?.presentation?.successImageUrl?.trim() || DEFAULT_SUCCESS_IMAGE;
39519
+ return { title, message, imageUrl, closeNote: this.t('sentCloseNote') };
39521
39520
  });
39522
39521
  this.loadErrorState = computed(() => {
39523
39522
  this.i18n.lang();
@@ -39722,19 +39721,16 @@ class SurveyResponseComponent {
39722
39721
  } @else if (alreadyAnswered()) {
39723
39722
  <val-empty-state [props]="alreadyAnsweredState()" />
39724
39723
  } @else if (sent()) {
39725
- @if (successPresentation(); as success) {
39726
- <section class="survey-response survey-response__success" aria-live="polite">
39727
- @if (success.imageUrl) {
39728
- <img class="survey-response__success-image" [src]="success.imageUrl" [alt]="success.title" />
39729
- }
39724
+ <section class="survey-response survey-response__success" aria-live="polite">
39725
+ @if (successPresentation(); as success) {
39726
+ <img class="survey-response__success-image" [src]="success.imageUrl" [alt]="success.title" />
39730
39727
  <val-display [props]="{ content: success.title, size: 'small', color: 'dark' }" />
39731
39728
  @if (success.message) {
39732
39729
  <val-title [props]="{ content: success.message, size: 'large', color: '', bold: false }" />
39733
39730
  }
39734
- </section>
39735
- } @else {
39736
- <val-empty-state [props]="sentState()" />
39737
- }
39731
+ <p class="survey-response__success-note">{{ success.closeNote }}</p>
39732
+ }
39733
+ </section>
39738
39734
  } @else {
39739
39735
  @if (typeConfig(); as cfg) {
39740
39736
  @if (!canRespond()) {
@@ -39774,7 +39770,7 @@ class SurveyResponseComponent {
39774
39770
  }
39775
39771
  }
39776
39772
  }
39777
- `, 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__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"] }] }); }
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"] }] }); }
39778
39774
  }
39779
39775
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyResponseComponent, decorators: [{
39780
39776
  type: Component,
@@ -39802,19 +39798,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
39802
39798
  } @else if (alreadyAnswered()) {
39803
39799
  <val-empty-state [props]="alreadyAnsweredState()" />
39804
39800
  } @else if (sent()) {
39805
- @if (successPresentation(); as success) {
39806
- <section class="survey-response survey-response__success" aria-live="polite">
39807
- @if (success.imageUrl) {
39808
- <img class="survey-response__success-image" [src]="success.imageUrl" [alt]="success.title" />
39809
- }
39801
+ <section class="survey-response survey-response__success" aria-live="polite">
39802
+ @if (successPresentation(); as success) {
39803
+ <img class="survey-response__success-image" [src]="success.imageUrl" [alt]="success.title" />
39810
39804
  <val-display [props]="{ content: success.title, size: 'small', color: 'dark' }" />
39811
39805
  @if (success.message) {
39812
39806
  <val-title [props]="{ content: success.message, size: 'large', color: '', bold: false }" />
39813
39807
  }
39814
- </section>
39815
- } @else {
39816
- <val-empty-state [props]="sentState()" />
39817
- }
39808
+ <p class="survey-response__success-note">{{ success.closeNote }}</p>
39809
+ }
39810
+ </section>
39818
39811
  } @else {
39819
39812
  @if (typeConfig(); as cfg) {
39820
39813
  @if (!canRespond()) {
@@ -39854,7 +39847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
39854
39847
  }
39855
39848
  }
39856
39849
  }
39857
- `, 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__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"] }]
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"] }]
39858
39851
  }], ctorParameters: () => [], propDecorators: { props: [{
39859
39852
  type: Input
39860
39853
  }], submitted: [{
@@ -40745,6 +40738,18 @@ const SURVEY_BUILDER_I18N = {
40745
40738
  emptyQuestionLabel: 'Todas las preguntas necesitan texto',
40746
40739
  saveError: 'No pudimos guardar la encuesta. Inténtalo de nuevo en un rato.',
40747
40740
  loadError: 'No pudimos cargar la encuesta para editar.',
40741
+ stepStart: 'Inicio',
40742
+ stepQuestions: 'Preguntas',
40743
+ stepClose: 'Cierre',
40744
+ stepReview: 'Revisar',
40745
+ next: 'Siguiente',
40746
+ previous: 'Volver',
40747
+ reviewTitle: 'Resumen',
40748
+ reviewNoSubtitle: 'Sin subtítulo',
40749
+ reviewQuestionsCount: '{count} preguntas',
40750
+ reviewHeaderImage: 'Imagen inicial configurada',
40751
+ reviewSuccessImage: 'Imagen de cierre configurada',
40752
+ reviewDefaultClose: 'Cierre por defecto',
40748
40753
  },
40749
40754
  en: {
40750
40755
  titleLabel: 'Title',
@@ -40784,6 +40789,18 @@ const SURVEY_BUILDER_I18N = {
40784
40789
  emptyQuestionLabel: 'Every question needs text',
40785
40790
  saveError: "We couldn't save the survey. Please try again in a while.",
40786
40791
  loadError: "We couldn't load the survey to edit.",
40792
+ stepStart: 'Start',
40793
+ stepQuestions: 'Questions',
40794
+ stepClose: 'Close',
40795
+ stepReview: 'Review',
40796
+ next: 'Next',
40797
+ previous: 'Back',
40798
+ reviewTitle: 'Summary',
40799
+ reviewNoSubtitle: 'No subtitle',
40800
+ reviewQuestionsCount: '{count} questions',
40801
+ reviewHeaderImage: 'Opening image set',
40802
+ reviewSuccessImage: 'Closing image set',
40803
+ reviewDefaultClose: 'Default closing screen',
40787
40804
  },
40788
40805
  };
40789
40806
 
@@ -40831,9 +40848,16 @@ class SurveyBuilderComponent {
40831
40848
  this.successImageControl = new FormControl('', { nonNullable: true });
40832
40849
  this.questions = signal([]);
40833
40850
  this.editingId = signal('');
40851
+ this.wizardStep = signal(0);
40834
40852
  this.validationError = signal('');
40835
40853
  this.saving = signal(false);
40836
40854
  this.uploadingImage = signal('');
40855
+ this.wizardSteps = [
40856
+ { id: 'start', labelKey: 'stepStart' },
40857
+ { id: 'questions', labelKey: 'stepQuestions' },
40858
+ { id: 'close', labelKey: 'stepClose' },
40859
+ { id: 'review', labelKey: 'stepReview' },
40860
+ ];
40837
40861
  this.allowedImageTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
40838
40862
  this.maxImageBytes = 10 * 1024 * 1024;
40839
40863
  if (!this.i18n.hasNamespace(NAMESPACE$1)) {
@@ -40878,6 +40902,75 @@ class SurveyBuilderComponent {
40878
40902
  questionTypes() {
40879
40903
  return this.props.questionTypes?.length ? this.props.questionTypes : SURVEY_QUESTION_TYPES;
40880
40904
  }
40905
+ isWizard() {
40906
+ return this.props.layout === 'wizard';
40907
+ }
40908
+ showWizardSection(step) {
40909
+ return this.isWizard() && this.wizardStep() === step;
40910
+ }
40911
+ goToStep(step) {
40912
+ if (!this.isWizard())
40913
+ return;
40914
+ if (step <= this.wizardStep()) {
40915
+ this.validationError.set('');
40916
+ this.wizardStep.set(step);
40917
+ return;
40918
+ }
40919
+ while (this.wizardStep() < step) {
40920
+ if (!this.validateWizardStep(this.wizardStep()))
40921
+ return;
40922
+ this.wizardStep.update(current => current + 1);
40923
+ }
40924
+ }
40925
+ nextStep() {
40926
+ if (!this.validateWizardStep(this.wizardStep()))
40927
+ return;
40928
+ this.wizardStep.update(step => Math.min(step + 1, this.wizardSteps.length - 1));
40929
+ }
40930
+ previousStep() {
40931
+ this.validationError.set('');
40932
+ this.wizardStep.update(step => Math.max(step - 1, 0));
40933
+ }
40934
+ wizardBackProps() {
40935
+ return {
40936
+ token: 'survey-builder-wizard-back',
40937
+ text: this.t('previous'),
40938
+ color: 'dark',
40939
+ fill: 'outline',
40940
+ shape: 'round',
40941
+ size: 'default',
40942
+ type: 'button',
40943
+ state: ComponentStates.ENABLED,
40944
+ };
40945
+ }
40946
+ wizardNextProps() {
40947
+ return {
40948
+ token: 'survey-builder-wizard-next',
40949
+ text: this.t('next'),
40950
+ color: 'dark',
40951
+ fill: 'solid',
40952
+ shape: 'round',
40953
+ size: 'default',
40954
+ type: 'button',
40955
+ state: ComponentStates.ENABLED,
40956
+ };
40957
+ }
40958
+ saveProps() {
40959
+ return {
40960
+ token: 'survey-builder-save',
40961
+ text: this.t('save'),
40962
+ color: 'dark',
40963
+ fill: 'solid',
40964
+ shape: 'round',
40965
+ size: 'large',
40966
+ expand: this.isWizard() ? undefined : 'block',
40967
+ type: 'button',
40968
+ state: this.saving() ? ComponentStates.WORKING : ComponentStates.ENABLED,
40969
+ };
40970
+ }
40971
+ interpolate(template, values) {
40972
+ return Object.entries(values).reduce((text, [key, value]) => text.replaceAll(`{${key}}`, String(value)), template);
40973
+ }
40881
40974
  editorProps(row) {
40882
40975
  return { mode: 'edit', field: row, allowedTypes: this.questionTypes() };
40883
40976
  }
@@ -41013,27 +41106,10 @@ class SurveyBuilderComponent {
41013
41106
  if (this.saving())
41014
41107
  return;
41015
41108
  this.validationError.set('');
41016
- const title = this.titleControl.value.trim();
41017
- if (!title) {
41018
- this.titleControl.markAsTouched();
41019
- this.validationError.set(this.t('noTitle'));
41109
+ if (!this.validateWizardStep(0) || !this.validateWizardStep(1))
41020
41110
  return;
41021
- }
41111
+ const title = this.titleControl.value.trim();
41022
41112
  const rows = this.questions();
41023
- if (!rows.length) {
41024
- this.validationError.set(this.t('noQuestions'));
41025
- return;
41026
- }
41027
- if (rows.some(r => !r.label.trim())) {
41028
- this.validationError.set(this.t('emptyQuestionLabel'));
41029
- return;
41030
- }
41031
- // El editor ya lo valida al cerrar cada pregunta, pero una pregunta abierta
41032
- // (o cargada de una encuesta vieja) puede llegar acá sin opciones.
41033
- if (rows.some(r => FIELD_TYPES_WITH_OPTIONS.includes(r.type) && !r.options?.length)) {
41034
- this.validationError.set(this.t('missingOptions'));
41035
- return;
41036
- }
41037
41113
  const fieldSchema = [];
41038
41114
  rows.forEach((row, index) => {
41039
41115
  const name = this.formBuilder.uniqueFieldName(row.name || row.label, fieldSchema);
@@ -41107,6 +41183,35 @@ class SurveyBuilderComponent {
41107
41183
  };
41108
41184
  return Object.values(presentation).some(Boolean) ? presentation : undefined;
41109
41185
  }
41186
+ validateWizardStep(step) {
41187
+ this.validationError.set('');
41188
+ if (step === 0) {
41189
+ if (!this.titleControl.value.trim()) {
41190
+ this.titleControl.markAsTouched();
41191
+ this.validationError.set(this.t('noTitle'));
41192
+ return false;
41193
+ }
41194
+ return true;
41195
+ }
41196
+ if (step === 1) {
41197
+ const rows = this.questions();
41198
+ if (!rows.length) {
41199
+ this.validationError.set(this.t('noQuestions'));
41200
+ return false;
41201
+ }
41202
+ if (rows.some(r => !r.label.trim())) {
41203
+ this.validationError.set(this.t('emptyQuestionLabel'));
41204
+ return false;
41205
+ }
41206
+ // El editor ya lo valida al cerrar cada pregunta, pero una pregunta abierta
41207
+ // (o cargada de una encuesta vieja) puede llegar acá sin opciones.
41208
+ if (rows.some(r => FIELD_TYPES_WITH_OPTIONS.includes(r.type) && !r.options?.length)) {
41209
+ this.validationError.set(this.t('missingOptions'));
41210
+ return false;
41211
+ }
41212
+ }
41213
+ return true;
41214
+ }
41110
41215
  rowFromField(f) {
41111
41216
  return {
41112
41217
  id: `row-${rowUid++}`,
@@ -41122,15 +41227,81 @@ class SurveyBuilderComponent {
41122
41227
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
41123
41228
  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: `
41124
41229
  <div class="survey-builder">
41125
- <val-form-field [label]="t('titleLabel')">
41126
- <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41127
- </val-form-field>
41128
- <val-form-field [label]="t('subtitleLabel')">
41129
- <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41130
- </val-form-field>
41230
+ @if (isWizard()) {
41231
+ <nav class="survey-builder__steps" aria-label="Survey builder steps">
41232
+ @for (step of wizardSteps; track step.id; let i = $index) {
41233
+ <button
41234
+ type="button"
41235
+ class="survey-builder__step"
41236
+ [class.survey-builder__step--active]="wizardStep() === i"
41237
+ [attr.aria-current]="wizardStep() === i ? 'step' : null"
41238
+ (click)="goToStep(i)"
41239
+ >
41240
+ <span>{{ i + 1 }}</span>
41241
+ {{ t(step.labelKey) }}
41242
+ </button>
41243
+ }
41244
+ </nav>
41245
+ }
41131
41246
 
41132
- <div class="survey-builder__presentation">
41133
- <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41247
+ @if (!isWizard()) {
41248
+ <val-form-field [label]="t('titleLabel')">
41249
+ <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41250
+ </val-form-field>
41251
+ <val-form-field [label]="t('subtitleLabel')">
41252
+ <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41253
+ </val-form-field>
41254
+ }
41255
+
41256
+ @if (showWizardSection(0)) {
41257
+ <section class="survey-builder__section">
41258
+ <val-form-field [label]="t('titleLabel')">
41259
+ <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41260
+ </val-form-field>
41261
+ <val-form-field [label]="t('subtitleLabel')">
41262
+ <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41263
+ </val-form-field>
41264
+ <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41265
+ <div class="survey-builder__image-field">
41266
+ <val-form-field [label]="t('headerImageLabel')">
41267
+ <val-text-input [props]="{ control: headerImageControl, placeholder: t('headerImagePlaceholder') }" />
41268
+ </val-form-field>
41269
+ @if (headerImageControl.value) {
41270
+ <div class="survey-builder__image-preview">
41271
+ <img [src]="headerImageControl.value" alt="" />
41272
+ <button
41273
+ type="button"
41274
+ class="survey-builder__image-clear"
41275
+ [attr.aria-label]="t('imageRemove')"
41276
+ (click)="clearPresentationImage('header')"
41277
+ >
41278
+ <ion-icon name="trash-outline" aria-hidden="true" />
41279
+ </button>
41280
+ </div>
41281
+ }
41282
+ @if (props.presentationImageUpload) {
41283
+ <label class="survey-builder__image-upload" [class.survey-builder__image-upload--loading]="uploadingImage() === 'header'">
41284
+ @if (uploadingImage() === 'header') {
41285
+ <ion-spinner name="crescent" />
41286
+ } @else {
41287
+ <ion-icon name="cloud-upload-outline" aria-hidden="true" />
41288
+ }
41289
+ <span>{{ headerImageControl.value ? t('imageChange') : t('imageUpload') }}</span>
41290
+ <input
41291
+ type="file"
41292
+ accept="image/jpeg,image/png,image/webp,image/gif,.jpg,.jpeg,.png,.webp,.gif"
41293
+ [disabled]="uploadingImage() === 'header'"
41294
+ (change)="onPresentationFileSelected('header', $event)"
41295
+ />
41296
+ </label>
41297
+ }
41298
+ </div>
41299
+ </section>
41300
+ }
41301
+
41302
+ @if (!isWizard()) {
41303
+ <div class="survey-builder__presentation">
41304
+ <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41134
41305
  <div class="survey-builder__image-field">
41135
41306
  <val-form-field [label]="t('headerImageLabel')">
41136
41307
  <val-text-input [props]="{ control: headerImageControl, placeholder: t('headerImagePlaceholder') }" />
@@ -41205,8 +41376,56 @@ class SurveyBuilderComponent {
41205
41376
  </label>
41206
41377
  }
41207
41378
  </div>
41208
- </div>
41379
+ </div>
41380
+ }
41209
41381
 
41382
+ @if (showWizardSection(2)) {
41383
+ <section class="survey-builder__section">
41384
+ <span class="survey-builder__questions-label">{{ t('stepClose') }}</span>
41385
+ <val-form-field [label]="t('successTitleLabel')">
41386
+ <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41387
+ </val-form-field>
41388
+ <val-form-field [label]="t('successMessageLabel')">
41389
+ <val-text-input [props]="{ control: successMessageControl, placeholder: t('successMessagePlaceholder') }" />
41390
+ </val-form-field>
41391
+ <div class="survey-builder__image-field">
41392
+ <val-form-field [label]="t('successImageLabel')">
41393
+ <val-text-input [props]="{ control: successImageControl, placeholder: t('successImagePlaceholder') }" />
41394
+ </val-form-field>
41395
+ @if (successImageControl.value) {
41396
+ <div class="survey-builder__image-preview">
41397
+ <img [src]="successImageControl.value" alt="" />
41398
+ <button
41399
+ type="button"
41400
+ class="survey-builder__image-clear"
41401
+ [attr.aria-label]="t('imageRemove')"
41402
+ (click)="clearPresentationImage('success')"
41403
+ >
41404
+ <ion-icon name="trash-outline" aria-hidden="true" />
41405
+ </button>
41406
+ </div>
41407
+ }
41408
+ @if (props.presentationImageUpload) {
41409
+ <label class="survey-builder__image-upload" [class.survey-builder__image-upload--loading]="uploadingImage() === 'success'">
41410
+ @if (uploadingImage() === 'success') {
41411
+ <ion-spinner name="crescent" />
41412
+ } @else {
41413
+ <ion-icon name="cloud-upload-outline" aria-hidden="true" />
41414
+ }
41415
+ <span>{{ successImageControl.value ? t('imageChange') : t('imageUpload') }}</span>
41416
+ <input
41417
+ type="file"
41418
+ accept="image/jpeg,image/png,image/webp,image/gif,.jpg,.jpeg,.png,.webp,.gif"
41419
+ [disabled]="uploadingImage() === 'success'"
41420
+ (change)="onPresentationFileSelected('success', $event)"
41421
+ />
41422
+ </label>
41423
+ }
41424
+ </div>
41425
+ </section>
41426
+ }
41427
+
41428
+ @if (showWizardSection(1)) {
41210
41429
  <div class="survey-builder__questions">
41211
41430
  <span class="survey-builder__questions-label">{{ t('questionsLabel') }}</span>
41212
41431
 
@@ -41286,27 +41505,41 @@ class SurveyBuilderComponent {
41286
41505
  (onClick)="addQuestion()"
41287
41506
  />
41288
41507
  </div>
41508
+ }
41509
+
41510
+ @if (showWizardSection(3)) {
41511
+ <section class="survey-builder__review">
41512
+ <span class="survey-builder__questions-label">{{ t('reviewTitle') }}</span>
41513
+ <strong>{{ titleControl.value || t('untitledQuestion') }}</strong>
41514
+ <span>{{ subtitleControl.value || t('reviewNoSubtitle') }}</span>
41515
+ <span>{{ interpolate(t('reviewQuestionsCount'), { count: questions().length }) }}</span>
41516
+ @if (headerImageControl.value) {
41517
+ <span>{{ t('reviewHeaderImage') }}</span>
41518
+ }
41519
+ <span>{{ successImageControl.value ? t('reviewSuccessImage') : t('reviewDefaultClose') }}</span>
41520
+ </section>
41521
+ }
41289
41522
 
41290
41523
  @if (validationError()) {
41291
41524
  <p class="survey-builder__error">{{ validationError() }}</p>
41292
41525
  }
41293
41526
 
41294
- <val-button
41295
- [props]="{
41296
- token: 'survey-builder-save',
41297
- text: t('save'),
41298
- color: 'dark',
41299
- fill: 'solid',
41300
- shape: 'round',
41301
- size: 'large',
41302
- expand: 'block',
41303
- type: 'button',
41304
- state: saving() ? 'WORKING' : 'ENABLED',
41305
- }"
41306
- (onClick)="save()"
41307
- />
41527
+ @if (isWizard()) {
41528
+ <div class="survey-builder__wizard-actions">
41529
+ @if (wizardStep() > 0) {
41530
+ <val-button [props]="wizardBackProps()" (onClick)="previousStep()" />
41531
+ }
41532
+ @if (wizardStep() < wizardSteps.length - 1) {
41533
+ <val-button [props]="wizardNextProps()" (onClick)="nextStep()" />
41534
+ } @else {
41535
+ <val-button [props]="saveProps()" (onClick)="save()" />
41536
+ }
41537
+ </div>
41538
+ } @else {
41539
+ <val-button [props]="saveProps()" (onClick)="save()" />
41540
+ }
41308
41541
  </div>
41309
- `, 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__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-border-color, rgba(0, 0, 0, .16));border-radius:20px;color:var(--ion-color-medium-shade, #6b6675);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"] }] }); }
41542
+ `, 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}.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-border-color, rgba(0, 0, 0, .16));border-radius:20px;color:var(--ion-color-medium-shade, #6b6675);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"] }] }); }
41310
41543
  }
41311
41544
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SurveyBuilderComponent, decorators: [{
41312
41545
  type: Component,
@@ -41321,15 +41554,81 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41321
41554
  FieldSchemaEditorComponent,
41322
41555
  ], template: `
41323
41556
  <div class="survey-builder">
41324
- <val-form-field [label]="t('titleLabel')">
41325
- <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41326
- </val-form-field>
41327
- <val-form-field [label]="t('subtitleLabel')">
41328
- <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41329
- </val-form-field>
41557
+ @if (isWizard()) {
41558
+ <nav class="survey-builder__steps" aria-label="Survey builder steps">
41559
+ @for (step of wizardSteps; track step.id; let i = $index) {
41560
+ <button
41561
+ type="button"
41562
+ class="survey-builder__step"
41563
+ [class.survey-builder__step--active]="wizardStep() === i"
41564
+ [attr.aria-current]="wizardStep() === i ? 'step' : null"
41565
+ (click)="goToStep(i)"
41566
+ >
41567
+ <span>{{ i + 1 }}</span>
41568
+ {{ t(step.labelKey) }}
41569
+ </button>
41570
+ }
41571
+ </nav>
41572
+ }
41330
41573
 
41331
- <div class="survey-builder__presentation">
41332
- <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41574
+ @if (!isWizard()) {
41575
+ <val-form-field [label]="t('titleLabel')">
41576
+ <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41577
+ </val-form-field>
41578
+ <val-form-field [label]="t('subtitleLabel')">
41579
+ <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41580
+ </val-form-field>
41581
+ }
41582
+
41583
+ @if (showWizardSection(0)) {
41584
+ <section class="survey-builder__section">
41585
+ <val-form-field [label]="t('titleLabel')">
41586
+ <val-text-input [props]="{ control: titleControl, placeholder: t('titlePlaceholder') }" />
41587
+ </val-form-field>
41588
+ <val-form-field [label]="t('subtitleLabel')">
41589
+ <val-text-input [props]="{ control: subtitleControl, placeholder: t('subtitlePlaceholder') }" />
41590
+ </val-form-field>
41591
+ <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41592
+ <div class="survey-builder__image-field">
41593
+ <val-form-field [label]="t('headerImageLabel')">
41594
+ <val-text-input [props]="{ control: headerImageControl, placeholder: t('headerImagePlaceholder') }" />
41595
+ </val-form-field>
41596
+ @if (headerImageControl.value) {
41597
+ <div class="survey-builder__image-preview">
41598
+ <img [src]="headerImageControl.value" alt="" />
41599
+ <button
41600
+ type="button"
41601
+ class="survey-builder__image-clear"
41602
+ [attr.aria-label]="t('imageRemove')"
41603
+ (click)="clearPresentationImage('header')"
41604
+ >
41605
+ <ion-icon name="trash-outline" aria-hidden="true" />
41606
+ </button>
41607
+ </div>
41608
+ }
41609
+ @if (props.presentationImageUpload) {
41610
+ <label class="survey-builder__image-upload" [class.survey-builder__image-upload--loading]="uploadingImage() === 'header'">
41611
+ @if (uploadingImage() === 'header') {
41612
+ <ion-spinner name="crescent" />
41613
+ } @else {
41614
+ <ion-icon name="cloud-upload-outline" aria-hidden="true" />
41615
+ }
41616
+ <span>{{ headerImageControl.value ? t('imageChange') : t('imageUpload') }}</span>
41617
+ <input
41618
+ type="file"
41619
+ accept="image/jpeg,image/png,image/webp,image/gif,.jpg,.jpeg,.png,.webp,.gif"
41620
+ [disabled]="uploadingImage() === 'header'"
41621
+ (change)="onPresentationFileSelected('header', $event)"
41622
+ />
41623
+ </label>
41624
+ }
41625
+ </div>
41626
+ </section>
41627
+ }
41628
+
41629
+ @if (!isWizard()) {
41630
+ <div class="survey-builder__presentation">
41631
+ <span class="survey-builder__questions-label">{{ t('presentationLabel') }}</span>
41333
41632
  <div class="survey-builder__image-field">
41334
41633
  <val-form-field [label]="t('headerImageLabel')">
41335
41634
  <val-text-input [props]="{ control: headerImageControl, placeholder: t('headerImagePlaceholder') }" />
@@ -41404,8 +41703,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41404
41703
  </label>
41405
41704
  }
41406
41705
  </div>
41407
- </div>
41706
+ </div>
41707
+ }
41408
41708
 
41709
+ @if (showWizardSection(2)) {
41710
+ <section class="survey-builder__section">
41711
+ <span class="survey-builder__questions-label">{{ t('stepClose') }}</span>
41712
+ <val-form-field [label]="t('successTitleLabel')">
41713
+ <val-text-input [props]="{ control: successTitleControl, placeholder: t('successTitlePlaceholder') }" />
41714
+ </val-form-field>
41715
+ <val-form-field [label]="t('successMessageLabel')">
41716
+ <val-text-input [props]="{ control: successMessageControl, placeholder: t('successMessagePlaceholder') }" />
41717
+ </val-form-field>
41718
+ <div class="survey-builder__image-field">
41719
+ <val-form-field [label]="t('successImageLabel')">
41720
+ <val-text-input [props]="{ control: successImageControl, placeholder: t('successImagePlaceholder') }" />
41721
+ </val-form-field>
41722
+ @if (successImageControl.value) {
41723
+ <div class="survey-builder__image-preview">
41724
+ <img [src]="successImageControl.value" alt="" />
41725
+ <button
41726
+ type="button"
41727
+ class="survey-builder__image-clear"
41728
+ [attr.aria-label]="t('imageRemove')"
41729
+ (click)="clearPresentationImage('success')"
41730
+ >
41731
+ <ion-icon name="trash-outline" aria-hidden="true" />
41732
+ </button>
41733
+ </div>
41734
+ }
41735
+ @if (props.presentationImageUpload) {
41736
+ <label class="survey-builder__image-upload" [class.survey-builder__image-upload--loading]="uploadingImage() === 'success'">
41737
+ @if (uploadingImage() === 'success') {
41738
+ <ion-spinner name="crescent" />
41739
+ } @else {
41740
+ <ion-icon name="cloud-upload-outline" aria-hidden="true" />
41741
+ }
41742
+ <span>{{ successImageControl.value ? t('imageChange') : t('imageUpload') }}</span>
41743
+ <input
41744
+ type="file"
41745
+ accept="image/jpeg,image/png,image/webp,image/gif,.jpg,.jpeg,.png,.webp,.gif"
41746
+ [disabled]="uploadingImage() === 'success'"
41747
+ (change)="onPresentationFileSelected('success', $event)"
41748
+ />
41749
+ </label>
41750
+ }
41751
+ </div>
41752
+ </section>
41753
+ }
41754
+
41755
+ @if (showWizardSection(1)) {
41409
41756
  <div class="survey-builder__questions">
41410
41757
  <span class="survey-builder__questions-label">{{ t('questionsLabel') }}</span>
41411
41758
 
@@ -41485,27 +41832,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
41485
41832
  (onClick)="addQuestion()"
41486
41833
  />
41487
41834
  </div>
41835
+ }
41836
+
41837
+ @if (showWizardSection(3)) {
41838
+ <section class="survey-builder__review">
41839
+ <span class="survey-builder__questions-label">{{ t('reviewTitle') }}</span>
41840
+ <strong>{{ titleControl.value || t('untitledQuestion') }}</strong>
41841
+ <span>{{ subtitleControl.value || t('reviewNoSubtitle') }}</span>
41842
+ <span>{{ interpolate(t('reviewQuestionsCount'), { count: questions().length }) }}</span>
41843
+ @if (headerImageControl.value) {
41844
+ <span>{{ t('reviewHeaderImage') }}</span>
41845
+ }
41846
+ <span>{{ successImageControl.value ? t('reviewSuccessImage') : t('reviewDefaultClose') }}</span>
41847
+ </section>
41848
+ }
41488
41849
 
41489
41850
  @if (validationError()) {
41490
41851
  <p class="survey-builder__error">{{ validationError() }}</p>
41491
41852
  }
41492
41853
 
41493
- <val-button
41494
- [props]="{
41495
- token: 'survey-builder-save',
41496
- text: t('save'),
41497
- color: 'dark',
41498
- fill: 'solid',
41499
- shape: 'round',
41500
- size: 'large',
41501
- expand: 'block',
41502
- type: 'button',
41503
- state: saving() ? 'WORKING' : 'ENABLED',
41504
- }"
41505
- (onClick)="save()"
41506
- />
41854
+ @if (isWizard()) {
41855
+ <div class="survey-builder__wizard-actions">
41856
+ @if (wizardStep() > 0) {
41857
+ <val-button [props]="wizardBackProps()" (onClick)="previousStep()" />
41858
+ }
41859
+ @if (wizardStep() < wizardSteps.length - 1) {
41860
+ <val-button [props]="wizardNextProps()" (onClick)="nextStep()" />
41861
+ } @else {
41862
+ <val-button [props]="saveProps()" (onClick)="save()" />
41863
+ }
41864
+ </div>
41865
+ } @else {
41866
+ <val-button [props]="saveProps()" (onClick)="save()" />
41867
+ }
41507
41868
  </div>
41508
- `, 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__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-border-color, rgba(0, 0, 0, .16));border-radius:20px;color:var(--ion-color-medium-shade, #6b6675);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"] }]
41869
+ `, 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}.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-border-color, rgba(0, 0, 0, .16));border-radius:20px;color:var(--ion-color-medium-shade, #6b6675);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"] }]
41509
41870
  }], ctorParameters: () => [], propDecorators: { props: [{
41510
41871
  type: Input
41511
41872
  }], saved: [{