diff --git a/app/Http/Controllers/FormController.php b/app/Http/Controllers/FormController.php index dd760f2..5ead730 100644 --- a/app/Http/Controllers/FormController.php +++ b/app/Http/Controllers/FormController.php @@ -1,6 +1,7 @@ questions; -foreach ($questions as $question) { - $question->options = json_decode($question->options, true); -} + { + // Questions are already fetched with their options cast to array due to the casts property + $questions = $form->questions; + foreach ($questions as $question) { + $question->options = json_decode($question->options, true); + } -// Pass the questions to the view -return view('forms.edit', compact('form', 'questions')); -} + // Pass the questions to the view + return view('forms.edit', compact('form', 'questions')); + } @@ -72,15 +74,14 @@ return view('forms.edit', compact('form', 'questions')); public function show(Form $form) { $form->load('questions.responses'); - return view('forms.show', compact('form')); } public function preview($id) -{ - $form = Form::findOrFail($id); - return view('forms.previewForm', compact('form')); -} + { + $form = Form::findOrFail($id); + return view('forms.previewForm', compact('form')); + } public function update(Request $request, Form $form) diff --git a/app/Http/Controllers/ResponseController.php b/app/Http/Controllers/ResponseController.php index 85b0ae2..ab2fe34 100644 --- a/app/Http/Controllers/ResponseController.php +++ b/app/Http/Controllers/ResponseController.php @@ -32,9 +32,28 @@ class ResponseController extends Controller // Get all questions for the form $questions = Question::where('form_id', $form->id)->get()->keyBy('id'); - return view('responses.viewResponse', compact('form', 'responses', 'questions')); + // Aggregate data for statistics + $statistics = []; + foreach ($questions as $question) { + $statistics[$question->id] = [ + 'question_text' => $question->question_text, + 'type' => $question->type, + 'options' => json_decode($question->options), + 'responses' => [] + ]; + + foreach ($responses as $response) { + $decodedAnswers = json_decode($response->answers, true); + if (isset($decodedAnswers[$question->id])) { + $statistics[$question->id]['responses'][] = $decodedAnswers[$question->id]; + } + } + } + + return view('responses.viewResponse', compact('form', 'responses', 'questions', 'statistics')); } + public function viewResponses(Form $form) { // Get all responses for the form, grouped by response_id @@ -43,9 +62,33 @@ public function viewResponses(Form $form) ->get() ->groupBy('response_id'); - return view('responses.viewResponses', compact('form', 'responses')); + // Get all questions for the form + $questions = Question::where('form_id', $form->id)->get()->keyBy('id'); + + // Aggregate data for statistics + $statistics = []; + foreach ($questions as $question) { + $statistics[$question->id] = [ + 'question_text' => $question->question_text, + 'type' => $question->type, + 'options' => json_decode($question->options, true), + 'responses' => [], + ]; + + foreach ($responses as $responseGroup) { + foreach ($responseGroup as $response) { + $decodedAnswers = json_decode($response->answers, true); + if (isset($decodedAnswers[$question->id])) { + $statistics[$question->id]['responses'][] = $decodedAnswers[$question->id]; + } + } + } + } + + return view('responses.viewResponses', compact('form', 'responses', 'statistics')); } + public function showForm(Form $form) { $questions = $form->questions; diff --git a/public/js/statistics.js b/public/js/statistics.js new file mode 100644 index 0000000..901b9b5 --- /dev/null +++ b/public/js/statistics.js @@ -0,0 +1,129 @@ +// statistics.js + +// Function to fetch responses from backend +function fetchResponses() { + return fetch('/api/responses') // Replace with your actual API endpoint + .then(response => response.json()) + .catch(error => console.error('Error fetching responses:', error)); +} + +// Function to process fetched responses data +function processDataForCharts(responses, questions) { + const pieData = {}; + const barData = {}; + + responses.forEach(response => { + const question = questions[response.question_id]; + const decodedAnswers = JSON.parse(response.answers); + + // Process data for pie chart + if (!pieData[question.question_text]) { + pieData[question.question_text] = {}; + } + if (question.type === 'multiple_choice' || question.type === 'checkbox') { + JSON.parse(question.options).forEach(option => { + pieData[question.question_text][option] = (pieData[question.question_text][option] || 0) + + (decodedAnswers.includes(option) ? 1 : 0); + }); + } + + // Process data for bar graph (assuming numerical responses) + if (question.type === 'short_answer' || question.type === 'long_answer') { + if (!barData[question.question_text]) { + barData[question.question_text] = { total: 0, count: 0 }; + } + barData[question.question_text].total += parseFloat(response.answers); + barData[question.question_text].count++; + } + }); + + // Calculate averages for bar graph + Object.keys(barData).forEach(key => { + barData[key].average = barData[key].total / barData[key].count; + }); + + return { pieData, barData }; +} + +// Function to render charts using Chart.js +function renderCharts(pieData, barData) { + // Render Pie Chart + const pieChartCanvas = document.getElementById('pieChart'); + new Chart(pieChartCanvas.getContext('2d'), { + type: 'pie', + data: { + labels: Object.keys(pieData), + datasets: Object.keys(pieData).map(question => ({ + label: question, + data: Object.values(pieData[question]), + backgroundColor: getRandomColors(Object.values(pieData[question]).length), + })), + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + tooltip: { + callbacks: { + label: function(context) { + let label = context.label || ''; + if (context.parsed.y !== null) { + label += ': ' + context.parsed.y; + } + return label; + }, + }, + }, + }, + }, + }); + + // Render Bar Graph + const barGraphCanvas = document.getElementById('barGraph'); + new Chart(barGraphCanvas.getContext('2d'), { + type: 'bar', + data: { + labels: Object.keys(barData), + datasets: [{ + label: 'Average Response', + data: Object.values(barData).map(item => item.average.toFixed(2)), + backgroundColor: getRandomColors(Object.keys(barData).length), + }], + }, + options: { + responsive: true, + scales: { + y: { + beginAtZero: true, + }, + }, + }, + }); +} + +// Function to generate random colors +function getRandomColors(count) { + const colors = []; + for (let i = 0; i < count; i++) { + colors.push(`rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.2)`); + } + return colors; +} + +// Main function to fetch data and render charts +async function main() { + try { + const responses = await fetchResponses(); + const questions = {!! json_encode($questions) !!}; // This assumes $questions is passed to the Blade view from Laravel + + const { pieData, barData } = processDataForCharts(responses, questions); + renderCharts(pieData, barData); + } catch (error) { + console.error('Error processing or rendering charts:', error); + } +} + +// Run main function on page load +window.addEventListener('DOMContentLoaded', main); diff --git a/resources/views/forms/edit.blade.php b/resources/views/forms/edit.blade.php index a3fb83e..59e3489 100644 --- a/resources/views/forms/edit.blade.php +++ b/resources/views/forms/edit.blade.php @@ -10,9 +10,9 @@ - - + -
@@ -93,21 +20,6 @@

{{ $form->title }} - Response Detail

-
-
- - - -
    -
  • - -
  • -
  • - -
  • -
-
-
@@ -122,13 +34,11 @@
- @foreach ($responses as $response) @php $question = $questions[$response->question_id] ?? null; $decodedAnswers = json_decode($response->answers, true); @endphp - @if ($question)

{{ $question->question_text }}

@@ -159,18 +69,6 @@ @endforeach
- -
-

Statistics

- -
- -
- -
- -
-
@@ -178,80 +76,6 @@ Return to Forms
- - - - - - - diff --git a/resources/views/responses/viewResponses.blade.php b/resources/views/responses/viewResponses.blade.php index 24ac7e9..a8f075a 100644 --- a/resources/views/responses/viewResponses.blade.php +++ b/resources/views/responses/viewResponses.blade.php @@ -8,6 +8,7 @@ Form Responses + @@ -25,63 +26,89 @@ User +
+ +
- -
-

Responses

-
- + +
+ + +
+

Responses

+
+   - - -
+ + + @if ($responses->isEmpty()) +

No responses available.

+ @else +
+ + + + + + + + + + @foreach ($responses as $responseGroup) + + + + + + @endforeach + +
UserSubmitted AtActions
+ {{ $responseGroup->first()->user->name ?? 'Anonymous' }} + + {{ $responseGroup->first()->created_at->diffForHumans()}} + + View Response +
+
+ @endif
- - @if ($responses->isEmpty()) -

No responses available.

- @else -
- - - - - - - - - - @foreach ($responses as $responseGroup) - - - - - - @endforeach - -
UserSubmitted AtActions
- {{ $responseGroup->first()->user->name ?? 'Anonymous' }} - - {{ $responseGroup->first()->created_at->diffForHumans()}} - - View Response -
+ + - @endif
@@ -99,6 +126,79 @@ copyNotification.classList.add("hidden"); }, 2000); } + + function showTab(tabId) { + document.querySelectorAll('.tab-content').forEach(tab => { + tab.classList.add('hidden'); + }); + document.getElementById(tabId).classList.remove('hidden'); + + document.querySelectorAll('.tab-link').forEach(link => { + link.classList.remove('border-purple-600', 'text-purple-600'); + }); + document.querySelector(`[onclick="showTab('${tabId}')"]`).classList.add('border-purple-600', 'text-purple-600'); + } + + // Generate Charts + document.addEventListener('DOMContentLoaded', function () { + const statistics = @json($statistics); + console.log(statistics); // Log statistics for debugging + + Object.keys(statistics).forEach(questionId => { + const ctx = document.getElementById(`chart-${questionId}`).getContext('2d'); + const stat = statistics[questionId]; + + let labels = []; + let data = []; + let backgroundColors = []; + + if (stat.type === 'multiple_choice' || stat.type === 'checkbox' || stat.type === 'dropdown') { + const optionCounts = {}; + stat.responses.forEach(response => { + if (Array.isArray(response)) { + response.forEach(option => { + if (option in optionCounts) { + optionCounts[option]++; + } else { + optionCounts[option] = 1; + } + }); + } else { + if (response in optionCounts) { + optionCounts[response]++; + } else { + optionCounts[response] = 1; + } + } + }); + + labels = Object.keys(optionCounts); + data = Object.values(optionCounts); + backgroundColors = labels.map((_, index) => `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.5)`); + } + + new Chart(ctx, { + type: 'bar', + data: { + labels: labels, + datasets: [{ + label: '# of Responses', + data: data, + backgroundColor: backgroundColors, + borderColor: backgroundColors.map(color => color.replace('0.5', '1')), + borderWidth: 1 + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + } + } + }); + }); + });