diff --git a/app/Http/Controllers/Siswa/DashboardController.php b/app/Http/Controllers/Siswa/DashboardController.php
new file mode 100644
index 0000000..dc33d7a
--- /dev/null
+++ b/app/Http/Controllers/Siswa/DashboardController.php
@@ -0,0 +1,109 @@
+middleware('auth:siswa');
+ }
+
+ public function index()
+ {
+ /** @var \App\Models\Siswa $siswa */
+ $siswa = Auth::guard('siswa')->user();
+
+ // =============================================
+ // 1. TUGAS โ ambil tugas yang belum dikumpulkan
+ // dan deadline-nya belum lewat, urutkan by deadline
+ // =============================================
+ $tugasRaw = Tugas::with('mataPelajaran') // eager load relasi mapel
+ ->whereDoesntHave('pengumpulan', function ($q) use ($siswa) {
+ // tugas yang BELUM dikumpulkan oleh siswa ini
+ $q->where('siswa_id', $siswa->id);
+ })
+ ->where('deadline', '>=', Carbon::now())
+ ->orderBy('deadline', 'asc')
+ ->take(5) // tampilkan maks 5 tugas di dashboard
+ ->get();
+
+ // Kelompokkan tugas berdasarkan tanggal deadline
+ $tugasList = [];
+ foreach ($tugasRaw as $tugas) {
+ $tgl = Carbon::parse($tugas->deadline)
+ ->locale('id')
+ ->isoFormat('dddd, D MMMM YYYY'); // contoh: "Sabtu, 10 Mei 2025"
+
+ $tugasList[$tgl][] = [
+ 'jam' => Carbon::parse($tugas->deadline)->format('H.i'),
+ 'nama' => $tugas->judul,
+ // sesuaikan nama kolom dengan model kamu
+ 'mapel' => 'Belum ยท ' . ($tugas->mataPelajaran->nama ?? '-'),
+ ];
+ }
+
+ // =============================================
+ // 2. CHALLENGE MINGGUAN
+ // =============================================
+ $startMingguIni = Carbon::now()->startOfWeek();
+ $endMingguIni = Carbon::now()->endOfWeek();
+
+ // Total challenge aktif minggu ini
+ $challengeTotal = Challenge::whereBetween('created_at', [$startMingguIni, $endMingguIni])
+ ->where('is_active', true)
+ ->count();
+
+ // Challenge yang sudah diselesaikan siswa minggu ini
+ $challengeDone = ChallengeSiswa::where('siswa_id', $siswa->id)
+ ->where('status', 'selesai')
+ ->whereBetween('created_at', [$startMingguIni, $endMingguIni])
+ ->count();
+
+ // =============================================
+ // 3. TUGAS SELESAI MINGGU INI (untuk speech bubble mascot)
+ // =============================================
+ $tugasSelesai = TugasSiswa::where('siswa_id', $siswa->id)
+ ->whereBetween('created_at', [$startMingguIni, $endMingguIni])
+ ->count();
+
+ // =============================================
+ // 4. LEADERBOARD โ top 3 siswa berdasarkan total EXP
+ // Asumsi: kolom 'exp' ada di tabel siswa
+ // Sesuaikan jika EXP disimpan di tabel lain
+ // =============================================
+ $leaderboardRaw = Siswa::orderBy('exp', 'desc')
+ ->take(3)
+ ->get();
+
+ $leaderboard = $leaderboardRaw->map(function ($item, $index) {
+ return [
+ 'rank' => $index + 1,
+ 'nama' => $item->nama,
+ 'exp' => $item->exp,
+ ];
+ })->toArray();
+
+ return view('siswa.dashboard', compact(
+ 'tugasList',
+ 'challengeDone',
+ 'challengeTotal',
+ 'tugasSelesai',
+ 'leaderboard',
+ ));
+ }
+}
\ No newline at end of file
diff --git a/resources/views/siswa/dashboard.blade.php b/resources/views/siswa/dashboard.blade.php
index 674cce6..71b468b 100644
--- a/resources/views/siswa/dashboard.blade.php
+++ b/resources/views/siswa/dashboard.blade.php
@@ -1,75 +1,498 @@
-@extends('siswa.layouts.app')
+@extends('layouts.siswa.app')
@section('title', 'Dashboard Siswa')
+@push('styles')
+
+@endpush
+
@section('content')
-
-
Dashboard
+@php $namaSaya = Auth::guard('siswa')->user()->nama ?? ''; @endphp
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {{-- ===== TUGAS ===== --}}
+
+
-
-
-
-
-
-
-
-
Challenge Selesai
-
0
+ @forelse($tugasList as $tanggal => $items)
+
{{ $tanggal }}
+ @foreach($items as $item)
+
+
{{ $item['jam'] }}
+
๐
+
+
{{ $item['nama'] }}
+
{{ $item['mapel'] }}
-
-
-
-
-
-
-
-
-
-
-
-
-
+ @endforeach
+ @empty
+
๐ Tidak ada tugas yang pending!
+ @endforelse
-
-
-
Informasi Akun
-
-
- Nama
- : {{ Auth::guard('siswa')->user()->nama }}
-
-
- NISN
- : {{ Auth::guard('siswa')->user()->nisn }}
-
-
- Kelas
- : {{ Auth::guard('siswa')->user()->kelas->nama_kelas ?? '-' }}
-
+ {{-- ===== KALENDER ===== --}}
+
+
+ ‹
+
+ ›
+
+
+
+
+ SUN MON TUE
+ WED THU FRI SAT
+
+
+
+
+ {{-- ===== CHALLENGE MINGGUAN ===== --}}
+
+
+
+ @php
+ $persen = $challengeTotal > 0
+ ? round(($challengeDone / $challengeTotal) * 100)
+ : 0;
+ @endphp
+
+
+
โก
+
+
Ayo kerjakan challenge dan dapatkan EXP tambahan!
+
+
{{ $challengeDone }}/{{ $challengeTotal }}
+
+
+
+
+
+
+ {{-- ===== MASCOT + SPEECH BUBBLE ===== --}}
+
+
+ @if($tugasSelesai > 0)
+ Kamu sudah mengerjakan {{ $tugasSelesai }} tugas minggu
+ ini, yuk lanjutkan untuk mendapatkan badge yang lebih menarik!
+ @else
+ Belum ada tugas yang diselesaikan minggu ini.
+ Ayo mulai kerjakan tugasmu! ๐ช
+ @endif
+
+
+ @if(file_exists(public_path('images/mascot.png')))
+
+ @else
+
๐ถ
+ @endif
+
+
+ {{-- ===== LEADERBOARD ===== --}}
+
+
+
+ @forelse($leaderboard as $lb)
+
+
+ @if($lb['rank'] === 1) ๐ฅ
+ @elseif($lb['rank'] === 2) ๐ฅ
+ @else ๐ฅ
+ @endif
+
+
{{ strtoupper(substr($lb['nama'], 0, 1)) }}
+
+ {{ $lb['nama'] }}
+
+
{{ number_format($lb['exp']) }} EXP
+
+ @empty
+
+ Belum ada data leaderboard.
+
+ @endforelse
+
+
-@endsection
\ No newline at end of file
+@endsection
+
+@push('scripts')
+
+@endpush
\ No newline at end of file
diff --git a/resources/views/siswa/layouts/app.blade.php b/resources/views/siswa/layouts/app.blade.php
index 6c8f4ef..4f53f55 100644
--- a/resources/views/siswa/layouts/app.blade.php
+++ b/resources/views/siswa/layouts/app.blade.php
@@ -16,30 +16,36 @@
}
.siswa-wrapper {
- display: flex;
- min-height: 100vh;
+ display: flex;
+ min-height: 100vh;
+ position: relative;
}
+ /* ===== SIDEBAR ===== */
.sidebar {
- width: 260px;
- background: #ffffff;
- border-right: 2px solid #e6f0ff;
- padding: 30px 20px;
- display: flex;
- flex-direction: column;
- transition: all 0.3s ease;
+ width: 260px;
+ background: #ffffff;
+ border-right: 2px solid #e6f0ff;
+ padding: 30px 20px;
+ display: flex;
+ flex-direction: column;
+ transition: width 0.3s ease, padding 0.3s ease;
+ overflow: hidden;
+ flex-shrink: 0;
+ position: relative;
}
+ /* Default: collapsed saat pertama load */
.sidebar.collapsed {
- width: 0;
- padding: 0;
- overflow: hidden;
- border: none;
+ width: 0;
+ padding: 0;
+ border-right: none;
}
.sidebar-logo {
text-align: center;
margin-bottom: 40px;
+ white-space: nowrap;
}
.sidebar-logo img {
@@ -49,6 +55,7 @@
.sidebar-menu {
display: flex;
flex-direction: column;
+ white-space: nowrap;
}
.sidebar-link {
@@ -82,6 +89,7 @@
.sidebar-logout {
margin-top: auto;
+ white-space: nowrap;
}
.sidebar-logout button {
@@ -95,16 +103,60 @@
cursor: pointer;
}
- .main {
- flex: 1;
- background: #f5f9ff;
- display: flex;
- flex-direction: column;
- transition: all 0.3s ease;
+ /* ===== TOGGLE ARROW BUTTON ===== */
+ .sidebar-toggle-btn {
+ position: fixed;
+ top: 50%;
+ transform: translateY(-50%);
+ left: 260px; /* sama dengan lebar sidebar saat terbuka */
+ z-index: 1000;
+ width: 22px;
+ height: 48px;
+ background: #2b8ef3;
+ border: none;
+ border-radius: 0 10px 10px 0;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: left 0.3s ease, background 0.2s ease;
+ box-shadow: 2px 0 8px rgba(43, 142, 243, 0.3);
}
- .main.full {
- margin-left: 0;
+ .sidebar-toggle-btn:hover {
+ background: #1a7ae0;
+ }
+
+ /* Posisi tombol saat sidebar collapsed */
+ .sidebar-toggle-btn.collapsed {
+ left: 0;
+ }
+
+ /* Arrow icon SVG */
+ .toggle-arrow {
+ width: 12px;
+ height: 12px;
+ fill: none;
+ stroke: white;
+ stroke-width: 2.5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ transition: transform 0.3s ease;
+ }
+
+ /* Saat collapsed, panah mengarah ke kanan (buka) */
+ .sidebar-toggle-btn.collapsed .toggle-arrow {
+ transform: rotate(180deg);
+ }
+
+ /* ===== MAIN ===== */
+ .main {
+ flex: 1;
+ background: #f5f9ff;
+ display: flex;
+ flex-direction: column;
+ transition: all 0.3s ease;
+ min-width: 0; /* prevent overflow */
}
/* TOPBAR */
@@ -122,6 +174,9 @@
.topbar-left {
font-weight: 600;
font-size: 16px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
}
.topbar-right {
@@ -149,8 +204,8 @@
-
-