Survey Creation Issue

User
@extends('layouts.admin')
@section('plugins.Summernote', true)
@section('plugins.icheckBootstrap', true)
@section('plugins.TempusDominusBs4', true)

{{– Customize layout sections –}}
@section('subtitle', __('Create – Survey'))

{{– Content body: main page content –}}
@section('content_body')

{{ __('Create Surveys') }}

@if($errors->any())

@foreach ($errors->all() as $error)
{{ $error }}
@endforeach

@endif
@if(session()->has('message.level'))

{!! session('message.content') !!}

@endif

Loading…

{{ __('Loading survey creation…') }}

1. Survey

2. Pillers

3. Questions

{{ __('Create Survey') }}

Name

@{{ surveyError }}

{{ __('Allow comments on Survey') }}
{{ __('Allow comments on Pillers') }}
{{ __('Mark as External') }}

{{ __('Save and Next') }}

{{ __('Add Pillers') }}

{{ __('Piller') }} @{{ index + 1 }}

{{ __('Title') }} (@{{ lang.code.toUpperCase() }})

{{ __('Weight') }}

{{ __('Position') }}

{{ __('Add More Pillers') }}

{{ __('Save and Next') }}

{{ __('Add Questions') }}

{{ __('Select Piller') }}

{{ __('– Select Piller –') }}

@{{ pill.titles.en }}

@{{ getPillerTitle(selectedPillerId) }}

{{ __('Title') }} (@{{ lang.code.toUpperCase() }})

{{ __('Position') }}

{{ __('Add More Question') }}

{{ __('Submit All Questions') }}

@stop

{{– Push extra scripts –}}
@push('js')

<script>
window.availableLanguages = @json(getLanguages());
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
</script>
<script>
import { createApp } from 'https://unpkg.com/vue@3.5.13/dist/vue.esm-browser.prod.js'
  document.addEventListener('DOMContentLoaded', () => {
    const app = createApp({
    data() {
      return {
        message: 'Hello Vue!',
        loading: false,
        step: 1,
        survey: {
          name: '',
          options: []
        },
        surveyError: '',
        survey_id: null,
        availableLanguages: window.availableLanguages || [],
        pillers: [
      {
        titles: {}, // dynamically assigned
        weight: '',
        position: ''
      }
    ],
    selectedPillerId: '',
    questions: {},
      }
    },
    mounted() {
  // Init first piller titles
  this.availableLanguages.forEach(lang => {
    this.pillers[0].titles[lang.code] = '';
  });
},
    methods: {
      getPillerTitle(id) {
    const piller = this.pillers.find(p => p.id == id);
    return piller ? piller.titles.en : '';
},
addQuestion() {
    if (!this.questions[this.selectedPillerId]) {
        this.questions[this.selectedPillerId] = [];
    }

    if (this.questions[this.selectedPillerId].length >= 10) return;

    const newQuestion = {
        titles: {},
        position: ''
    };

    this.availableLanguages.forEach(lang => {
        newQuestion.titles[lang.code] = '';
    });

    this.questions[this.selectedPillerId].push(newQuestion);
},

removeQuestion(index) {
    this.questions[this.selectedPillerId].splice(index, 1);
},
      goToStep(step) {
    if (step === 2 && !this.survey_id) return;
    if (step === 3 && this.pillers.every(p => !p.id)) return;
    this.step = step;
  },
  addPiller() {
    if (this.pillers.length >= 5) return;
    const newPiller = {
      titles: {},
      weight: '',
      position: ''
    };
    this.availableLanguages.forEach(lang => {
      newPiller.titles[lang.code] = '';
    });
    this.pillers.push(newPiller);
  },
  removePiller(index) {
    this.pillers.splice(index, 1);
  },
  async submitSurvey() {
    
    this.surveyError = '';

    if (!this.survey.name) {
        this.surveyError = 'Survey name is required.';
        return;
    }

    const data = {
        name: this.survey.name,
        allow_comments: this.survey.options.includes('allow_comments') ? 1 : 0,
        allow_comments_on_pillers: this.survey.options.includes('allow_comments_on_pillers') ? 1 : 0,
        mark_as_external: this.survey.options.includes('mark_as_external') ? 1 : 0,
    };
    this.loading = true;
    try {
    fetch("{{ route('admin.surveys.store') }}", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "X-CSRF-TOKEN": csrfToken
        },
        body: JSON.stringify(data)
    })
    .then(res => res.json().then(json => ({ status: res.status, body: json })))
    .then(({ status, body }) => {
        if (status === 200 && body.success) {
            this.survey_id = body.survey_id;
            Swal.fire({
                icon: 'success',
                title: "{{ __('Survey saved successfully!') }}",
                showConfirmButton: false,
                timer: 1500
            });
            this.step = 2;
        } else {
            Swal.fire({
                icon: 'error',
                title: "{{ __('Validation Error') }}",
                text: body.message || "{{ __('Please check your input.') }}"
            });
        }
    })
    .catch(err => {
        Swal.fire({
            icon: 'error',
            title: "{{ __('Unexpected Error') }}",
            text: "{{ __('Something went wrong while saving the survey.') }}"
        });
    });
    } catch (error) {
      Swal.fire({
        icon: 'error',
        title: "{{ __('Unexpected Error') }}",
        text: "{{ __('Something went wrong while saving the survey.') }}"
      });
    } finally {
      this.loading = false;
    }
},

submitPillers() {
  if (this.pillers.length !== 5) {
    Swal.fire('Validation Error', 'You must add exactly 5 Pillers to continue.', 'warning');
    return;
  }

  // Validate each piller
  for (let [i, piller] of this.pillers.entries()) {
    for (let lang of this.availableLanguages) {
      if (!piller.titles[lang.code]) {
        Swal.fire('Validation Error', `Piller ${i + 1}: Title required for ${lang.title}`, 'error');
        return;
      }
    }
    if (!piller.weight || !piller.position) {
      Swal.fire('Validation Error', `Piller ${i + 1}: Weight and Position are required`, 'error');
      return;
    }
  }

  const data = {
    survey_id: this.survey_id,
    pillers: this.pillers
  };
  this.loading = true;
  try {
  fetch("{{ route('admin.pillars.store') }}", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-CSRF-TOKEN": csrfToken
    },
    body: JSON.stringify(data)
  })
  .then(res => res.json().then(json => ({ status: res.status, body: json })))
  .then(({ status, body }) => {
    if (status === 200 && body.success) {
      // Save returned piller_ids if needed
      this.pillers = body.pillers;
      Swal.fire({
        icon: 'success',
        title: "{{ __('Pillers saved successfully!') }}",
        showConfirmButton: false,
        timer: 1500
      });
      this.step = 3;
    } else {
      Swal.fire({
        icon: 'error',
        title: "{{ __('Validation Error') }}",
        text: body.message || "{{ __('Please check your input.') }}"
      });
    }
  })
  .catch(() => {
    Swal.fire({
      icon: 'error',
      title: "{{ __('Unexpected Error') }}",
      text: "{{ __('Something went wrong while saving the Pillers.') }}"
    });
  });
  } catch (error) {
    Swal.fire({
      icon: 'error',
      title: "{{ __('Unexpected Error') }}",
      text: "{{ __('Something went wrong while saving the Pillers.') }}"
    });
  } finally {
    this.loading = false;
  }
},

submitQuestions() {
    const payload = {
        questions: []
    };

    for (let piller of this.pillers) {
        const list = this.questions[piller.id] || [];

        if (list.length < 1) {
            Swal.fire('Validation Error', `Please add at least 1 question for "${piller.titles.en}"`, 'warning');
            return;
        }

        for (let q of list) {
            for (let lang of this.availableLanguages) {
                if (!q.titles[lang.code]) {
                    Swal.fire('Validation Error', `All languages required for a question in "${piller.titles.en}"`, 'error');
                    return;
                }
            }
            if (!q.position) {
                Swal.fire('Validation Error', `Position is required for a question in "${piller.titles.en}"`, 'error');
                return;
            }

            payload.questions.push({
                piller_id: piller.id,
                titles: q.titles,
                position: q.position
            });
        }
    }

    fetch("{{ route('admin.questions.store') }}", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "X-CSRF-TOKEN": "{{ csrf_token() }}"
        },
        body: JSON.stringify(payload)
    })
    .then(res => res.json().then(json => ({ status: res.status, body: json })))
    .then(({ status, body }) => {
        if (status === 200 && body.success) {
            Swal.fire({
                icon: 'success',
                title: "{{ __('Questions saved successfully!') }}",
                showConfirmButton: false,
                timer: 1500
            });
        } else {
            Swal.fire({
                icon: 'error',
                title: "{{ __('Validation Error') }}",
                text: body.message || "{{ __('Please check your input.') }}"
            });
        }
    })
    .catch(() => {
        Swal.fire({
            icon: 'error',
            title: "{{ __('Unexpected Error') }}",
            text: "{{ __('Something went wrong while saving the questions.') }}"
        });
    });
}
    }
  });
  app.mount('#surveyApp');
  // Hide loading spinner after mount
const loadingDiv = document.getElementById('vue-loading');
if (loadingDiv) {
  loadingDiv.classList.add('fade-out');
  setTimeout(() => loadingDiv.remove(), 500); // fully remove from DOM after fade
}
  });
</script>

@endpush
on select pillar in 3rd step no data showing even section is blank

Assistant
The issue you're facing — where **step 3 (Questions)** appears blank after selecting a Piller — is likely due to one of the following:

### ✅ **Likely Problem: `questions[piller.id]` is `undefined`**
In your `v-for` loop in step 3, you have:

<div v-for="(question, index) in questions[selectedPillerId]" :key="index" ...>

If `questions[selectedPillerId]` is undefined at the time of render, this will cause an error like:
> `TypeError: Cannot read properties of undefined (reading 'length')`

### ✅ **Fix: Initialize `questions[selectedPillerId]` when Piller is selected**

Modify your `watch` or `method` when selecting a piller so it ensures the selected piller has an initialized array in `questions`.

#### ✅ Add a watcher or method like this:

watch: {
  selectedPillerId(newVal) {
    if (newVal && !this.questions[newVal]) {
      this.$set(this.questions, newVal, []);
    }
  }
}

Or if you're using plain JS (not SFC/component style with watchers), do it in the `select` dropdown handler:

<select v-model="selectedPillerId" class="form-control" @change="onPillerChange">
methods: {
  onPillerChange() {
    if (this.selectedPillerId && !this.questions[this.selectedPillerId]) {
      this.questions[this.selectedPillerId] = [];
    }
  },
  ...
}

### ✅ Optional UI Fallback (Prevent Blank Step)
Wrap the questions list with a fallback check:

<div v-if="selectedPillerId && questions[selectedPillerId]">
  <!-- Your v-for here -->
</div>
<div v-else class="alert alert-info">
  {{ __('No questions added yet. Click "Add More Question" to begin.') }}
</div>

### 🔄 Summary of Fixes:
1. Ensure `questions[selectedPillerId]` is initialized when a piller is selected.
2. Wrap your rendering logic to handle empty or undefined gracefully.
3. (Optional) Show fallback message when no questions exist yet.

Would you like me to refactor and paste only the step 3 section with the fix applied?

User
show full code
Assistant
Here’s the updated **Step 3 (Questions)** section with the **fixes** applied for when a Piller is selected and no data shows (or page goes blank).

I’ve added:
– `@change="onPillerChange"` to the dropdown.
– A fallback `v-if` for safety.
– `onPillerChange()` method that initializes the `questions[selectedPillerId]`.

### ✅ **Updated Step 3 Template Section**

<!-- Step 3 -->
<div v-if="step === 3">
  <h4>{{ __('Add Questions') }}</h4>

  <!-- Piller Selector -->
  <div class="form-group">
    <label>{{ __('Select Piller') }}</label>
    <select v-model="selectedPillerId" class="form-control" @change="onPillerChange">
      <option disabled value="">{{ __('-- Select Piller --') }}</option>
      <option v-for="pill in pillers" :key="pill.id" :value="pill.id">
        @{{ pill.titles.en }}
      </option>
    </select>
  </div>

  <!-- Question Entry for Selected Piller -->
  <div v-if="selectedPillerId && questions[selectedPillerId]" class="border p-3 mb-3">
    <h5>@{{ getPillerTitle(selectedPillerId) }}</h5>

    <div v-for="(question, index) in questions[selectedPillerId]" :key="index" class="form-row align-items-end mb-2">
      <div v-for="lang in availableLanguages" :key="lang.code" class="col-md-4">
        <label>{{ __('Title') }} (@{{ lang.code.toUpperCase() }})</label>
        <input type="text" v-model="question.titles[lang.code]" class="form-control" :placeholder="`Enter title in ${lang.title}`">
      </div>
      <div class="col-md-2">
        <label>{{ __('Position') }}</label>
        <input type="number" v-model="question.position" class="form-control" placeholder="1">
      </div>
      <div class="col-md-1">
        <button class="btn btn-danger btn-sm mt-4" @click="removeQuestion(index)">
          <i class="fa fa-trash"></i>
        </button>
      </div>
    </div>

    <button class="btn btn-primary" @click="addQuestion" :disabled="questions[selectedPillerId].length >= 10">
      {{ __('Add More Question') }}
    </button>
  </div>

  <div v-else class="alert alert-info">
    {{ __('Select a Piller and click "Add More Question" to begin.') }}
  </div>

  <div class="text-right mt-4">
    <button class="btn btn-success" @click="submitQuestions">
      {{ __('Submit All Questions') }}
    </button>
  </div>
</div>

### ✅ **Add this method in `methods:` section**

onPillerChange() {
  if (this.selectedPillerId && !this.questions[this.selectedPillerId]) {
    this.questions[this.selectedPillerId] = [];
  }
}

This will ensure the `questions[selectedPillerId]` array is initialized when a Piller is selected — avoiding the "undefined" issue and allowing dynamic question inputs to render.

Let me know if you want this part also reflected in your full Vue inline script or want to modularize it later.

Leave a Comment

Your email address will not be published. Required fields are marked *