From ffdae460383387538dc68d075935850f1a1203e1 Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Thu, 20 Aug 2026 15:44:56 +0330 Subject: [PATCH] feat: add survey --- .../Controllers/SurveyQuestionController.php | 57 +++++++++++++++++++ app/Models/User.php | 5 ++ routes/api.php | 3 + 3 files changed, 65 insertions(+) diff --git a/app/Http/Controllers/SurveyQuestionController.php b/app/Http/Controllers/SurveyQuestionController.php index 56ed0c8..30feff8 100644 --- a/app/Http/Controllers/SurveyQuestionController.php +++ b/app/Http/Controllers/SurveyQuestionController.php @@ -6,6 +6,7 @@ use App\Models\SurveyAnswer; use App\Models\SurveyQuestion; use App\Models\Tag; +use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Validation\ValidationException; @@ -56,6 +57,62 @@ public function adminShow($id) return response()->json($question); } + // ADMIN: paginated list of users with their survey stats (age, gender, answers count). + public function adminUsers(Request $request) + { + $perPage = max(1, min((int) $request->input('per_page', 20), 100)); + + $users = User::select('id', 'first_name', 'last_name', 'age', 'gender', 'email', 'mobile', 'created_at', 'identifier') + ->withCount(['surveyAnswers as answers_count']) + ->withCount(['surveyAnswers as questions_answered' => function ($q) { + $q->select(DB::raw('COUNT(DISTINCT survey_question_id)')); + }]) + ->orderByDesc('created_at') + ->paginate($perPage); + + return response()->json($users); + } + + // ADMIN: single user profile with all their survey answers grouped by question. + // Accepts either local user ID or approagency identifier (UUID). + public function adminUserShow($identifier) + { + $user = User::where('id', $identifier) + ->orWhere('identifier', $identifier) + ->select('id', 'first_name', 'last_name', 'age', 'gender', 'birthday', 'email', 'mobile', 'created_at') + ->withCount(['surveyAnswers as answers_count']) + ->withCount(['surveyAnswers as questions_answered' => function ($q) { + $q->select(DB::raw('COUNT(DISTINCT survey_question_id)')); + }]) + ->first(); + + if (!$user) { + abort(404); + } + + $answers = SurveyAnswer::where('user_id', $user->id) + ->with(['question.options', 'option']) + ->get() + ->groupBy('survey_question_id') + ->map(function ($group) { + $question = $group->first()->question; + return [ + 'question_id' => $question->id, + 'question' => $question->question, + 'type' => $question->type, + 'answers' => $group->map(fn ($a) => [ + 'option_id' => $a->survey_option_id, + 'label' => $a->option->label, + ])->values(), + ]; + }) + ->values(); + + $user->survey_answers = $answers; + + return response()->json($user); + } + // ADMIN: clean aggregated results — per option vote counts & percentages, no raw rows. // Pass an $id for one question, omit it for all questions. public function adminAnalytics($id = null) diff --git a/app/Models/User.php b/app/Models/User.php index b84d7c1..d514c5f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -143,4 +143,9 @@ public function savedMedia() return $this->belongsToMany(Media::class, 'saved_media')->withTimestamps(); } +public function surveyAnswers() +{ + return $this->hasMany(SurveyAnswer::class); +} + } \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index c1850fb..6f96402 100644 --- a/routes/api.php +++ b/routes/api.php @@ -166,6 +166,9 @@ Route::get('/admin/survey-questions/{id}/analytics', [SurveyQuestionController::class, 'adminAnalytics']); Route::get('/admin/survey-questions', [SurveyQuestionController::class, 'adminIndex']); Route::get('/admin/survey-questions/{id}', [SurveyQuestionController::class, 'adminShow']); + // User list + detail for survey admin + Route::get('/admin/survey-users', [SurveyQuestionController::class, 'adminUsers']); + Route::get('/admin/survey-users/{id}', [SurveyQuestionController::class, 'adminUserShow']); }); // User: answer + read questions with their own answers only. Route::get('/survey-questions/suggested-media', [SurveyQuestionController::class, 'suggestedMedia']);