diff --git a/app/Http/Controllers/Admin/DailyQuestController.php b/app/Http/Controllers/Admin/DailyQuestController.php new file mode 100644 index 0000000..19850fc --- /dev/null +++ b/app/Http/Controllers/Admin/DailyQuestController.php @@ -0,0 +1,85 @@ +paginate(15); + + // Upgraded Bank of Smart Suggestions (Hardcore Focused) + $suggestions = [ + // --- EXTREME CHALLENGES --- + ['title' => 'اسطوره لیگ: پیروزی در ۵۰ دوئل', 'quest_type' => 'duel_win', 'required_amount' => 50, 'reward_xp' => 4000], + ['title' => 'ماراتن تستزنی: پاسخ به ۵۰۰ تست', 'quest_type' => 'answer_tests', 'required_amount' => 500, 'reward_xp' => 3500], + ['title' => 'کمالگرا: کسب نمره کامل در ۵ آزمون', 'quest_type' => 'perfect_score', 'required_amount' => 5, 'reward_xp' => 2500], + + // --- HARD CHALLENGES --- + ['title' => 'مبارز خستگیناپذیر: پیروزی در ۲۰ دوئل', 'quest_type' => 'duel_win', 'required_amount' => 20, 'reward_xp' => 1500], + ['title' => 'چالش حقوقدان: پاسخ به ۲۵۰ تست', 'quest_type' => 'answer_tests', 'required_amount' => 250, 'reward_xp' => 1200], + ['title' => 'دقت بالا: کسب نمره کامل در ۳ آزمون', 'quest_type' => 'perfect_score', 'required_amount' => 3, 'reward_xp' => 1200], + ['title' => 'پیروزی در ۱۰ دوئل متوالی (کاپ نقره)', 'quest_type' => 'duel_win', 'required_amount' => 10, 'reward_xp' => 700], + + // --- MEDIUM CHALLENGES (For balance) --- + ['title' => 'گرمکردن: پیروزی در ۵ دوئل', 'quest_type' => 'duel_win', 'required_amount' => 5, 'reward_xp' => 300], + ['title' => 'مرور روزانه: پاسخ به ۵۰ تست', 'quest_type' => 'answer_tests', 'required_amount' => 50, 'reward_xp' => 250], + ['title' => 'کسب نمره کامل در ۱ آزمون', 'quest_type' => 'perfect_score', 'required_amount' => 1, 'reward_xp' => 350], + ]; + + return view('admin.daily-quests.index', compact('quests', 'suggestions')); + } + + public function create() + { + return view('admin.daily-quests.create'); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'title' => 'required|string|max:255', + 'quest_type' => 'required|string|in:duel_win,answer_tests,perfect_score', + 'required_amount' => 'required|integer|min:1', + 'reward_xp' => 'required|integer|min:10', + ]); + + $validated['is_active'] = $request->has('is_active'); // <-- Catch the switch + + DailyQuest::create($validated); + + return redirect()->route('daily-quests.index')->with('success', 'چالش روزانه با موفقیت ایجاد شد.'); + } + + public function update(Request $request, DailyQuest $daily_quest) + { + $validated = $request->validate([ + 'title' => 'required|string|max:255', + 'quest_type' => 'required|string|in:duel_win,answer_tests,perfect_score', + 'required_amount' => 'required|integer|min:1', + 'reward_xp' => 'required|integer|min:10', + ]); + + $validated['is_active'] = $request->has('is_active'); // <-- Catch the switch + + $daily_quest->update($validated); + + return redirect()->route('daily-quests.index')->with('success', 'چالش روزانه با موفقیت ویرایش شد.'); + } + + + public function edit(DailyQuest $daily_quest) + { + return view('admin.daily-quests.update', compact('daily_quest')); + } + + public function destroy(DailyQuest $daily_quest) + { + $daily_quest->delete(); + return redirect()->route('daily-quests.index')->with('success', 'چالش روزانه حذف شد.'); + } +} diff --git a/app/Http/Controllers/api/LeagueController.php b/app/Http/Controllers/api/LeagueController.php new file mode 100644 index 0000000..b410392 --- /dev/null +++ b/app/Http/Controllers/api/LeagueController.php @@ -0,0 +1,139 @@ +user(); + + // Dynamic Level Calculation (Every 500 XP = 1 Level) + $level = floor($user->xp / 500) + 1; + $xpToNext = 500 - ($user->xp % 500); + $progress = ($user->xp % 500) / 500; + + $winRate = $user->duels_played > 0 + ? round(($user->duels_won / $user->duels_played) * 100) + : 0; + + // Calculate Global Rank Position + $rankPosition = User::where('xp', '>', $user->xp)->count() + 1; + + $data = [ + 'user_id' => $user->id, + 'name' => $user->name ?? 'کاربر', + 'current_level' => $level, + 'rank_name' => $this->getRankName($level), + 'total_xp' => $user->xp, + 'xp_to_next_level' => $xpToNext, + 'progress_percent' => round($progress, 2), + 'win_rate_percent' => $winRate, + 'total_duels' => $user->duels_played, + 'global_rank_position' => $rankPosition + ]; + + return $this->success($data, 'موفق', 'اطلاعات لیگ کاربر دریافت شد'); + } + + /** + * API 2: Daily Quests + */ + public function quests(Request $request) + { + $user = $request->user(); + + // Fetch active quests for today using the new relationship + $userQuests = $user->activeQuests()->with('quest')->get(); + + $formattedQuests = $userQuests->map(function ($uq) { + return [ + 'id' => $uq->id, + 'title' => $uq->quest->title, + 'current_progress' => $uq->current_progress, + 'required_amount' => $uq->quest->required_amount, + 'reward_text' => '+' . $uq->quest->reward_xp . ' XP', + 'is_completed' => $uq->is_completed + ]; + }); + + $expiresIn = now()->endOfDay()->diffInSeconds(now()); + + return $this->success([ + 'expires_in_seconds' => $expiresIn, + 'quests' => $formattedQuests + ], 'موفق', 'چالشهای روزانه دریافت شد'); + } + + /** + * API 3: League Leaderboard + */ + public function leaderboard(Request $request) + { + $currentUser = $request->user(); + + // Get Top 50 Users + $topUsers = User::orderByDesc('xp')->limit(50)->get(['id', 'name', 'xp']); + + $leaderboard = $topUsers->map(function ($u, $index) use ($currentUser) { + return [ + 'rank' => $index + 1, + 'user_id' => $u->id, + 'name' => $u->name ?? 'کاربر ناشناس', + 'xp' => $u->xp, + 'is_me' => $u->id === $currentUser->id + ]; + }); + + // Current User Data + $myRank = User::where('xp', '>', $currentUser->xp)->count() + 1; + + $myStats = [ + 'rank' => $myRank, + 'user_id' => $currentUser->id, + 'name' => $currentUser->name ?? 'شما', + 'xp' => $currentUser->xp, + 'is_me' => true + ]; + + return $this->success([ + 'leaderboard' => $leaderboard, + 'current_user' => $myStats + ], 'موفق', 'جدول ردهبندی دریافت شد'); + } + + /** + * API 4: PvP Matchmaking Entry + */ + public function matchmake(Request $request) + { + $ticketId = 'req_' . Str::random(12); + + return $this->success([ + 'matchmaking_ticket_id' => $ticketId, + 'status' => 'searching', + 'websocket_channel' => 'private-matchmaking.' . $ticketId, + 'estimated_wait_seconds' => 15 + ], 'در حال جستجو', 'به صف انتظار دوئل اضافه شدید'); + } + + private function getRankName($level) + { + if ($level >= 50) return 'الماس'; + if ($level >= 30) return 'طلا'; + if ($level >= 10) return 'نقرهای'; + return 'برنز'; + } +} diff --git a/app/Models/DailyQuest.php b/app/Models/DailyQuest.php new file mode 100644 index 0000000..153bdb0 --- /dev/null +++ b/app/Models/DailyQuest.php @@ -0,0 +1,28 @@ + 'boolean', + ]; + + public function userQuests() + { + return $this->hasMany(UserQuest::class, 'daily_quest_id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 3a002cb..81ca141 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -21,6 +21,9 @@ class User extends Authenticatable 'is_admin', 'email', 'password', + 'xp', + 'duels_played', + 'duels_won', ]; protected $hidden = [ @@ -67,4 +70,25 @@ class User extends Authenticatable ->withPivot('read_at') ->withTimestamps(); } + + // ========================================== + // --- NEW: Gamification Relationships --- + // ========================================== + + /** + * Get all quest records for the user. + */ + public function userQuests() + { + return $this->hasMany(UserQuest::class); + } + + /** + * Helper relationship to quickly get ONLY active, unexpired quests. + */ + public function activeQuests() + { + return $this->hasMany(UserQuest::class) + ->where('expires_at', '>', now()); + } } diff --git a/app/Models/UserQuest.php b/app/Models/UserQuest.php new file mode 100644 index 0000000..4aaa3d4 --- /dev/null +++ b/app/Models/UserQuest.php @@ -0,0 +1,35 @@ + 'boolean', + 'expires_at' => 'datetime', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + public function quest() + { + return $this->belongsTo(DailyQuest::class, 'daily_quest_id'); + } +} diff --git a/resources/views/admin/daily-quests/create.blade.php b/resources/views/admin/daily-quests/create.blade.php new file mode 100644 index 0000000..2efff2c --- /dev/null +++ b/resources/views/admin/daily-quests/create.blade.php @@ -0,0 +1,47 @@ +@extends('admin.layouts.app') +@section('content') +
| شناسه | +عنوان چالش | +نوع چالش | +مقدار مورد نیاز | +پاداش (XP) | +وضعیت | +عملیات | +
|---|---|---|---|---|---|---|
| {{ $item->id }} | +{{ $item->title }} | +{{ $item->quest_type }} | +{{ $item->required_amount }} | ++{{ $item->reward_xp }} XP | ++ + {{ $item->is_active ? 'فعال' : 'غیرفعال' }} + + | ++ + ویرایش + | +