diff --git a/app/Http/Controllers/Siswa/ChallengeController.php b/app/Http/Controllers/Siswa/ChallengeController.php
index b62da20..adb57c3 100644
--- a/app/Http/Controllers/Siswa/ChallengeController.php
+++ b/app/Http/Controllers/Siswa/ChallengeController.php
@@ -3,9 +3,11 @@
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
+use App\Models\Badge;
use App\Models\Challenge;
use App\Models\PesertaChallenge;
use App\Models\Leaderboard;
+use App\Models\SiswaBadge;
use App\Services\BadgeService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -108,6 +110,11 @@ public function submit(Request $request, $id_challenge)
? $now->year . '/' . ($now->year + 1)
: ($now->year - 1) . '/' . $now->year;
+ // Snapshot badge sebelum submit — untuk deteksi badge baru
+ $badgeSebelum = SiswaBadge::where('id_siswa', $siswa->id_siswa)
+ ->pluck('id_badge')
+ ->toArray();
+
DB::transaction(function () use ($siswa, $challenge, $jawabanJson, $totalExp, $semester, $tahunAjaran) {
PesertaChallenge::create([
'id_challenge' => $challenge->id_challenge,
@@ -138,9 +145,22 @@ public function submit(Request $request, $id_challenge)
->each(fn($row, $i) => $row->update(['ranking' => $i + 1]));
});
- // --- Cek & berikan badge challenge (di luar transaksi agar tidak block) ---
+ // Cek & berikan badge challenge
app(BadgeService::class)->checkChallengeBadges($siswa->id_siswa);
+ // Deteksi badge yang baru didapat (selisih sebelum & sesudah)
+ $badgeSesudah = SiswaBadge::where('id_siswa', $siswa->id_siswa)
+ ->pluck('id_badge')
+ ->toArray();
+
+ $idBadgeBaru = array_diff($badgeSesudah, $badgeSebelum);
+
+ // Simpan ke session agar bisa ditampilkan di halaman hasil
+ if (!empty($idBadgeBaru)) {
+ $badgeBaru = Badge::whereIn('id_badge', $idBadgeBaru)->get();
+ session()->flash('badge_baru', $badgeBaru);
+ }
+
return redirect()->route('siswa.challenge.hasil', $id_challenge);
}
@@ -165,9 +185,13 @@ public function hasil($id_challenge)
$totalSoal = $challenge->soal->count();
$persentase = $totalSoal > 0 ? round(($benar / $totalSoal) * 100) : 0;
+ // Ambil badge baru dari session (hanya ada jika baru saja submit)
+ $badgeBaru = session('badge_baru', collect());
+
return view('siswa.challenge.hasil', compact(
'challenge', 'peserta', 'jawabanSiswa',
- 'benar', 'salah', 'totalSoal', 'persentase'
+ 'benar', 'salah', 'totalSoal', 'persentase',
+ 'badgeBaru'
));
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Siswa/LeaderboardController.php b/app/Http/Controllers/Siswa/LeaderboardController.php
index adb554f..c8b1fe6 100644
--- a/app/Http/Controllers/Siswa/LeaderboardController.php
+++ b/app/Http/Controllers/Siswa/LeaderboardController.php
@@ -4,6 +4,7 @@
use App\Http\Controllers\Controller;
use App\Models\Leaderboard;
+use App\Models\SiswaBadge;
use App\Services\BadgeService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
@@ -48,28 +49,42 @@ private function getData()
public function index()
{
$data = $this->getData();
- // Keluarkan 'siswa' dari data yang dikirim ke view (tidak dibutuhkan di view)
unset($data['siswa']);
return view('siswa.leaderboard.index', $data);
}
/**
* Endpoint JSON untuk polling real-time.
- * Setiap kali dipanggil, badge leaderboard siswa yang sedang login
- * dievaluasi ulang (diberikan atau dicabut sesuai ranking saat ini).
+ * Mengevaluasi badge leaderboard siswa, lalu return badge yang dimiliki
+ * saat ini agar JS bisa mendeteksi badge baru via localStorage.
*/
public function json()
{
$data = $this->getData();
$siswa = $data['siswa'];
- // Evaluasi badge leaderboard secara real-time untuk siswa yang sedang login
+ // Evaluasi badge leaderboard (grant/revoke)
app(BadgeService::class)->checkLeaderboardBadges(
$siswa->id_siswa,
$siswa->id_kelas
);
+ // Ambil semua badge leaderboard yang dimiliki siswa saat ini
+ // beserta detail badge (icon, nama, deskripsi) untuk ditampilkan di pop-up
+ $badgeSiswa = SiswaBadge::with('badge')
+ ->where('id_siswa', $siswa->id_siswa)
+ ->whereHas('badge', fn($q) => $q->whereIn('syarat', ['leaderboard_top5', 'leaderboard_top1']))
+ ->get()
+ ->map(fn($sb) => [
+ 'id_badge' => $sb->id_badge,
+ 'nama_badge' => $sb->badge->nama_badge,
+ 'deskripsi' => $sb->badge->deskripsi,
+ 'icon_url' => asset($sb->badge->icon_badge),
+ ]);
+
unset($data['siswa']);
+ $data['badgeSiswa'] = $badgeSiswa;
+
return response()->json($data);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Siswa/ProfileController.php b/app/Http/Controllers/Siswa/ProfileController.php
index eeadbaa..2736418 100644
--- a/app/Http/Controllers/Siswa/ProfileController.php
+++ b/app/Http/Controllers/Siswa/ProfileController.php
@@ -3,6 +3,8 @@
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
+use App\Models\Badge;
+use App\Models\SiswaBadge;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -10,6 +12,27 @@
class ProfileController extends Controller
{
+ public function edit()
+ {
+ $siswa = Auth::guard('siswa')->user();
+
+ // Semua badge yang ada di sistem
+ $semuaBadge = Badge::all();
+
+ // ID badge yang dimiliki siswa
+ $idBadgeDimiliki = SiswaBadge::where('id_siswa', $siswa->id_siswa)
+ ->pluck('id_badge')
+ ->toArray();
+
+ // Gabungkan: tiap badge + flag apakah siswa memilikinya
+ $badges = $semuaBadge->map(function ($badge) use ($idBadgeDimiliki) {
+ $badge->dimiliki = in_array($badge->id_badge, $idBadgeDimiliki);
+ return $badge;
+ });
+
+ return view('siswa.profile.edit', compact('badges'));
+ }
+
public function updateAjax(Request $request)
{
$siswa = Auth::guard('siswa')->user();
diff --git a/app/Http/Controllers/Siswa/TugasController.php b/app/Http/Controllers/Siswa/TugasController.php
index f8e9b93..fe717e3 100644
--- a/app/Http/Controllers/Siswa/TugasController.php
+++ b/app/Http/Controllers/Siswa/TugasController.php
@@ -3,13 +3,14 @@
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
+use App\Models\Badge;
+use App\Models\SiswaBadge;
+use App\Services\BadgeService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\Models\Tugas;
use App\Models\PengumpulanTugas;
-use App\Models\Mengajar;
-use App\Services\BadgeService;
class TugasController extends Controller
{
@@ -137,9 +138,26 @@ public function submit(Request $request, $id_tugas)
'status' => $status,
]);
- // --- Cek & berikan badge tugas hanya jika tepat waktu ---
+ // Cek & berikan badge tugas hanya jika tepat waktu
if ($status === 'dikumpulkan') {
+ // Snapshot badge sebelum pengecekan
+ $badgeSebelum = SiswaBadge::where('id_siswa', $siswa->id_siswa)
+ ->pluck('id_badge')
+ ->toArray();
+
app(BadgeService::class)->checkTugasBadges($siswa->id_siswa);
+
+ // Deteksi badge yang baru didapat
+ $badgeSesudah = SiswaBadge::where('id_siswa', $siswa->id_siswa)
+ ->pluck('id_badge')
+ ->toArray();
+
+ $idBadgeBaru = array_diff($badgeSesudah, $badgeSebelum);
+
+ if (!empty($idBadgeBaru)) {
+ $badgeBaru = Badge::whereIn('id_badge', $idBadgeBaru)->get();
+ session()->flash('badge_baru', $badgeBaru);
+ }
}
$pesan = $status === 'terlambat'
diff --git a/public/css/login.css b/public/css/login.css
index 6eb0e6d..bf1aae5 100644
--- a/public/css/login.css
+++ b/public/css/login.css
@@ -450,8 +450,9 @@ .other-portals a {
color: #5b3fc0;
font-weight: 700;
text-decoration: none;
- padding: 3px 8px;
+ padding: 5px 12px;
border-radius: 6px;
+ font-size: 15px;
transition: background 0.2s;
}
diff --git a/resources/views/siswa/challenge/hasil.blade.php b/resources/views/siswa/challenge/hasil.blade.php
index d03593e..6ff11b4 100644
--- a/resources/views/siswa/challenge/hasil.blade.php
+++ b/resources/views/siswa/challenge/hasil.blade.php
@@ -124,6 +124,98 @@
margin-top: 20px;
}
.btn-back:hover { opacity: 0.9; color: white; }
+
+/* ── Modal Badge ─────────────────────────────────────────── */
+.badge-modal-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ background: rgba(0,0,0,0.55);
+ z-index: 9999;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(3px);
+}
+
+.badge-modal-overlay.active {
+ display: flex;
+}
+
+.badge-modal {
+ background: white;
+ border-radius: 24px;
+ padding: 36px 28px 28px;
+ max-width: 380px;
+ width: 90%;
+ text-align: center;
+ animation: popIn 0.35s cubic-bezier(0.34,1.56,0.64,1);
+ position: relative;
+}
+
+@keyframes popIn {
+ from { transform: scale(0.7); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+}
+
+.badge-modal-label {
+ font-size: 12px;
+ font-weight: 700;
+ color: #7c3aed;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ margin-bottom: 20px;
+}
+
+.badge-modal-list {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ margin-bottom: 24px;
+}
+
+.badge-modal-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.badge-modal-icon {
+ width: 100%;
+ max-width: 220px;
+ height: auto;
+ object-fit: contain;
+ margin-bottom: 14px;
+}
+
+.badge-modal-info-nama {
+ font-size: 16px;
+ font-weight: 800;
+ color: #1e293b;
+ margin-bottom: 4px;
+}
+
+.badge-modal-info-desc {
+ font-size: 13px;
+ color: #64748b;
+}
+
+.badge-modal-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 12px 32px;
+ font-size: 14px;
+ font-weight: 700;
+ font-family: 'Poppins', sans-serif;
+ cursor: pointer;
+ width: 100%;
+ transition: opacity 0.2s;
+}
+
+.badge-modal-btn:hover { opacity: 0.9; }
+
@endpush
@@ -146,6 +238,33 @@
: ($persentase >= 60 ? 'Bagus! Terus tingkatkan kemampuanmu!' : 'Jangan menyerah! Terus semangat belajar!');
@endphp
+{{-- ── Pop-up Modal Badge Baru ── --}}
+@if(isset($badgeBaru) && $badgeBaru->isNotEmpty())
+
+
+
Badge Baru Diraih!
+
+
+ @foreach($badgeBaru as $b)
+
+
 }})
+
{{ $b->nama_badge }}
+
{{ $b->deskripsi }}
+
+ @endforeach
+
+
+
+
+
+@endif
+
diff --git a/resources/views/siswa/layouts/app.blade.php b/resources/views/siswa/layouts/app.blade.php
index f4eaf81..5bd635e 100644
--- a/resources/views/siswa/layouts/app.blade.php
+++ b/resources/views/siswa/layouts/app.blade.php
@@ -28,7 +28,6 @@
.sidebar-link.active { background: #e6f0ff; color: #1d4ed8; }
.sidebar-icon { width: 20px; height: 20px; flex-shrink: 0; object-fit: contain; }
- /* Logout — disamain dengan layout guru */
.sidebar-logout {
margin-top: auto;
padding-top: 16px;
@@ -131,6 +130,153 @@
.modal-toast { border-radius: 10px; padding: 10px 14px; font-size: 13px; font-weight: 500; margin-bottom: 13px; display: none; }
.modal-toast.success { background: #f0fdf4; border: 1.5px solid #86efac; color: #166534; display: block; }
.modal-toast.error { background: #fef2f2; border: 1.5px solid #fca5a5; color: #991b1b; display: block; }
+
+ /* ── Badge Koleksi di Modal ── */
+ .modal-badge-section { margin-top: 20px; }
+ .modal-badge-title {
+ font-size: 13px;
+ font-weight: 700;
+ color: #475569;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+ .modal-badge-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 10px;
+ }
+ .modal-badge-item {
+ background: #f8fafc;
+ border: 2px solid #e2e8f0;
+ border-radius: 14px;
+ padding: 12px 8px;
+ text-align: center;
+ position: relative;
+ transition: all 0.2s;
+ }
+ .modal-badge-item.dimiliki {
+ background: #faf5ff;
+ border-color: #c4b5fd;
+ }
+ .modal-badge-item.belum {
+ opacity: 0.45;
+ filter: grayscale(100%);
+ }
+ .modal-badge-icon {
+ width: 40px;
+ height: 40px;
+ object-fit: contain;
+ display: block;
+ margin: 0 auto 7px;
+ }
+ .modal-badge-nama {
+ font-size: 11px;
+ font-weight: 700;
+ color: #1e293b;
+ line-height: 1.3;
+ margin-bottom: 3px;
+ }
+ .modal-badge-desc {
+ font-size: 10px;
+ color: #94a3b8;
+ line-height: 1.3;
+ }
+ .modal-badge-lock {
+ position: absolute;
+ top: 7px;
+ right: 7px;
+ width: 13px;
+ height: 13px;
+ color: #94a3b8;
+ }
+ .modal-badge-tag {
+ display: inline-block;
+ background: #ede9fe;
+ color: #7c3aed;
+ font-size: 9px;
+ font-weight: 700;
+ padding: 2px 6px;
+ border-radius: 99px;
+ margin-top: 4px;
+ }
+
+ /* ── Modal Badge Global (leaderboard) ── */
+ .lb-badge-modal-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ background: rgba(0,0,0,0.55);
+ z-index: 99999;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(3px);
+ }
+ .lb-badge-modal-overlay.active { display: flex; }
+ .lb-badge-modal {
+ background: white;
+ border-radius: 24px;
+ padding: 32px 28px 24px;
+ max-width: 360px;
+ width: 90%;
+ text-align: center;
+ animation: lbPopIn 0.35s cubic-bezier(0.34,1.56,0.64,1);
+ }
+ @keyframes lbPopIn {
+ from { transform: scale(0.7); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+ }
+ .lb-badge-modal-label {
+ font-size: 12px;
+ font-weight: 700;
+ color: #7c3aed;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ margin-bottom: 20px;
+ }
+ .lb-badge-modal-list {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ margin-bottom: 24px;
+ }
+ .lb-badge-modal-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ }
+ .lb-badge-modal-icon {
+ width: 100%;
+ max-width: 220px;
+ height: auto;
+ object-fit: contain;
+ margin-bottom: 14px;
+ }
+ .lb-badge-modal-nama {
+ font-size: 16px;
+ font-weight: 800;
+ color: #1e293b;
+ margin-bottom: 4px;
+ }
+ .lb-badge-modal-desc { font-size: 13px; color: #64748b; }
+ .lb-badge-modal-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 12px 32px;
+ font-size: 14px;
+ font-weight: 700;
+ font-family: 'Poppins', sans-serif;
+ cursor: pointer;
+ width: 100%;
+ transition: opacity 0.2s;
+ }
+ .lb-badge-modal-btn:hover { opacity: 0.9; }
@stack('styles')
@@ -222,6 +368,13 @@
{{-- PROFILE MODAL --}}
+@php
+ use App\Models\Badge;
+ use App\Models\SiswaBadge;
+ $semuaBadgeLayout = Badge::all();
+ $idBadgeDimiliki = SiswaBadge::where('id_siswa', $siswa->id_siswa)->pluck('id_badge')->toArray();
+@endphp
+
+
+ {{-- ── Badge Koleksi ── --}}
+ @if($semuaBadgeLayout->isNotEmpty())
+
+
+
🏅 Badge Koleksiku
+
+ @foreach($semuaBadgeLayout as $b)
+ @php $dimiliki = in_array($b->id_badge, $idBadgeDimiliki); @endphp
+
+ @if(!$dimiliki)
+
+ @endif
+
 }})
+
{{ $b->nama_badge }}
+
{{ $b->deskripsi }}
+ @if($dimiliki)
+
✓ Diraih
+ @endif
+
+ @endforeach
+
+
+ @endif
+
+
+
+
+{{-- Modal Badge Leaderboard Global --}}
+
+
+
Badge Baru Diraih!
+
+
@@ -281,6 +472,9 @@ function updateTogglePosition(c){toggleBtn.style.left=c?'0px':SIDEBAR_W+'px';c?t
toggleBtn.addEventListener('click',function(){const c=sidebar.classList.contains('collapsed');sidebar.classList.toggle('collapsed');updateTogglePosition(!c);});
const NOTIF_URL='{{ route("siswa.notifikasi") }}';
+const LB_JSON_URL='{{ route("siswa.leaderboard.json") }}';
+const LB_BADGE_STORAGE_KEY='lb_badge_ids_{{ Auth::guard("siswa")->id() }}';
+
const notifIcons = {
materi: '
',
tugas: '
'
@@ -313,6 +507,83 @@ function toggleNotif(e){e.stopPropagation();document.getElementById('notifDropdo
document.addEventListener('click',e=>{if(!document.getElementById('notifWrap').contains(e.target))document.getElementById('notifDropdown').classList.remove('show');});
fetchNotif();setInterval(fetchNotif,30000);
+/* ── Polling badge leaderboard ── */
+const LB_BADGE_PENDING_KEY = LB_BADGE_STORAGE_KEY + '_pending';
+
+function getSavedBadgeIds() {
+ try { return JSON.parse(localStorage.getItem(LB_BADGE_STORAGE_KEY) || '[]'); } catch(e) { return []; }
+}
+function saveBadgeIds(ids) {
+ try { localStorage.setItem(LB_BADGE_STORAGE_KEY, JSON.stringify(ids)); } catch(e) {}
+}
+function getPendingBadges() {
+ try { return JSON.parse(localStorage.getItem(LB_BADGE_PENDING_KEY) || '[]'); } catch(e) { return []; }
+}
+function savePendingBadges(badges) {
+ try { localStorage.setItem(LB_BADGE_PENDING_KEY, JSON.stringify(badges)); } catch(e) {}
+}
+function clearPendingBadges() {
+ try { localStorage.removeItem(LB_BADGE_PENDING_KEY); } catch(e) {}
+}
+
+function showLbBadgeModal(badges) {
+ const list = document.getElementById('lbBadgeList');
+ list.innerHTML = badges.map(b => `
+
+

+
${b.nama_badge}
+
${b.deskripsi}
+
`).join('');
+ document.getElementById('lbBadgeModal').classList.add('active');
+}
+
+function dismissLbBadgeModal() {
+ document.getElementById('lbBadgeModal').classList.remove('active');
+ // Baru update storage setelah user dismiss — ini yang fix masalah
+ const pending = getPendingBadges();
+ if (pending.length > 0) {
+ saveBadgeIds(pending.map(b => b._confirmedIds).flat());
+ clearPendingBadges();
+ }
+}
+
+async function pollLbBadge() {
+ try {
+ const res = await fetch(LB_JSON_URL);
+ const data = await res.json();
+ const currentBadges = data.badgeSiswa || [];
+ const currentIds = currentBadges.map(b => b.id_badge);
+ const savedIds = getSavedBadgeIds();
+
+ // Badge baru = ada di current tapi belum di saved
+ const newBadges = currentBadges.filter(b => !savedIds.includes(b.id_badge));
+
+ if (newBadges.length > 0) {
+ // Simpan sebagai pending — storage belum diupdate sampai user dismiss
+ savePendingBadges([{ _confirmedIds: currentIds }]);
+ showLbBadgeModal(newBadges);
+ } else {
+ // Tidak ada badge baru, update storage langsung (termasuk handle badge dicabut)
+ saveBadgeIds(currentIds);
+ }
+ } catch(e) {}
+}
+// Jalankan polling badge leaderboard setiap 10 detik
+// Cek dulu apakah ada pending badge yang belum di-dismiss (dari halaman sebelumnya)
+(function checkPendingOnLoad() {
+ const pending = getPendingBadges();
+ if (pending.length > 0) {
+ // Ada badge yang belum di-dismiss, tampilkan ulang
+ // Ambil dari saved + current ids untuk reconstruct badge list
+ // Polling akan handle ini, tapi kita perlu tampilkan modal dulu
+ // Gunakan data dari pending storage untuk reconstruct
+ pollLbBadge(); // polling akan detect karena storage belum diupdate
+ return;
+ }
+ pollLbBadge();
+})();
+setInterval(pollLbBadge, 10000);
+
function openProfileModal(){document.getElementById('profileModalOverlay').classList.add('show');document.body.style.overflow='hidden';}
function closeProfileModal(){document.getElementById('profileModalOverlay').classList.remove('show');document.body.style.overflow='';const t=document.getElementById('modal-toast');t.className='modal-toast';t.textContent='';}
function closeOnOverlay(e){if(e.target===document.getElementById('profileModalOverlay'))closeProfileModal();}
diff --git a/resources/views/siswa/tugas/index.blade.php b/resources/views/siswa/tugas/index.blade.php
index e4c40e2..25cbaad 100644
--- a/resources/views/siswa/tugas/index.blade.php
+++ b/resources/views/siswa/tugas/index.blade.php
@@ -207,11 +207,129 @@
align-items: center;
gap: 8px;
}
+
+ /* ── Modal Badge ─────────────────────────────────────────── */
+ .badge-modal-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ background: rgba(0,0,0,0.55);
+ z-index: 9999;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(3px);
+ }
+
+ .badge-modal-overlay.active {
+ display: flex;
+ }
+
+ .badge-modal {
+ background: white;
+ border-radius: 24px;
+ padding: 36px 28px 28px;
+ max-width: 380px;
+ width: 90%;
+ text-align: center;
+ animation: popIn 0.35s cubic-bezier(0.34,1.56,0.64,1);
+ position: relative;
+ }
+
+ @keyframes popIn {
+ from { transform: scale(0.7); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+ }
+
+ .badge-modal-label {
+ font-size: 12px;
+ font-weight: 700;
+ color: #7c3aed;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ margin-bottom: 20px;
+ }
+
+ .badge-modal-list {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ margin-bottom: 24px;
+ }
+
+ .badge-modal-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ }
+
+ .badge-modal-icon {
+ width: 100%;
+ max-width: 220px;
+ height: auto;
+ object-fit: contain;
+ margin-bottom: 14px;
+ }
+
+ .badge-modal-info-nama {
+ font-size: 16px;
+ font-weight: 800;
+ color: #1e293b;
+ margin-bottom: 4px;
+ }
+
+ .badge-modal-info-desc {
+ font-size: 13px;
+ color: #64748b;
+ }
+
+ .badge-modal-btn {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 12px 32px;
+ font-size: 14px;
+ font-weight: 700;
+ font-family: 'Poppins', sans-serif;
+ cursor: pointer;
+ width: 100%;
+ transition: opacity 0.2s;
+ }
+
+ .badge-modal-btn:hover { opacity: 0.9; }
@endpush
@section('content')
+{{-- ── Pop-up Modal Badge Baru (tugas) ── --}}
+@if(session('badge_baru') && session('badge_baru')->isNotEmpty())
+
+
+
Badge Baru Diraih!
+
+
+ @foreach(session('badge_baru') as $b)
+
+
 }})
+
{{ $b->nama_badge }}
+
{{ $b->deskripsi }}
+
+ @endforeach
+
+
+
+
+
+@endif
+
Tugas