diff --git a/app/Http/Controllers/Admin/ProfileController.php b/app/Http/Controllers/Admin/ProfileController.php
index 87e7a3a..8341ba1 100644
--- a/app/Http/Controllers/Admin/ProfileController.php
+++ b/app/Http/Controllers/Admin/ProfileController.php
@@ -7,7 +7,6 @@
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
-use App\Models\Admin;
class ProfileController extends Controller
{
@@ -17,7 +16,7 @@ public function edit()
return view('admin.profile.edit', compact('admin'));
}
- public function update(Request $request)
+ public function updateAjax(Request $request)
{
$admin = Auth::guard('admin')->user();
@@ -41,17 +40,23 @@ public function update(Request $request)
$data['password'] = Hash::make($request->password);
}
+ $fotoUrl = null;
if ($request->hasFile('foto_profil')) {
if ($admin->foto_profil && Storage::disk('public')->exists($admin->foto_profil)) {
Storage::disk('public')->delete($admin->foto_profil);
}
$path = $request->file('foto_profil')->store('foto_profil/admin', 'public');
$data['foto_profil'] = $path;
+ $fotoUrl = Storage::url($path);
}
$admin->update($data);
- return redirect()->route('admin.profile.edit')
- ->with('success', 'Profil berhasil diperbarui!');
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Profil berhasil diperbarui!',
+ 'foto_url' => $fotoUrl ?? ($admin->foto_profil ? Storage::url($admin->foto_profil) : null),
+ 'username' => $admin->fresh()->username,
+ ]);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Guru/ProfileController.php b/app/Http/Controllers/Guru/ProfileController.php
index 31fb263..17f1f19 100644
--- a/app/Http/Controllers/Guru/ProfileController.php
+++ b/app/Http/Controllers/Guru/ProfileController.php
@@ -10,13 +10,7 @@
class ProfileController extends Controller
{
- public function edit()
- {
- $guru = Auth::guard('guru')->user();
- return view('guru.profile.edit', compact('guru'));
- }
-
- public function update(Request $request)
+ public function updateAjax(Request $request)
{
$guru = Auth::guard('guru')->user();
@@ -31,7 +25,8 @@ public function update(Request $request)
'foto_profil.max' => 'Ukuran foto maksimal 2MB.',
]);
- $data = [];
+ $data = [];
+ $fotoUrl = null;
if ($request->filled('password')) {
$data['password'] = Hash::make($request->password);
@@ -43,13 +38,17 @@ public function update(Request $request)
}
$path = $request->file('foto_profil')->store('foto_profil/guru', 'public');
$data['foto_profil'] = $path;
+ $fotoUrl = Storage::url($path);
}
if (!empty($data)) {
$guru->update($data);
}
- return redirect()->route('guru.profile.edit')
- ->with('success', 'Profil berhasil diperbarui!');
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Profil berhasil diperbarui!',
+ 'foto_url' => $fotoUrl ?? ($guru->foto_profil ? Storage::url($guru->foto_profil) : null),
+ ]);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Siswa/LeaderboardController.php b/app/Http/Controllers/Siswa/LeaderboardController.php
index 8f437dd..d07582d 100644
--- a/app/Http/Controllers/Siswa/LeaderboardController.php
+++ b/app/Http/Controllers/Siswa/LeaderboardController.php
@@ -5,11 +5,12 @@
use App\Http\Controllers\Controller;
use App\Models\Leaderboard;
use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
class LeaderboardController extends Controller
{
- public function index()
+ private function getData()
{
$siswa = Auth::guard('siswa')->user();
@@ -19,7 +20,6 @@ public function index()
? $now->year . '/' . ($now->year + 1)
: ($now->year - 1) . '/' . $now->year;
- // Top leaderboard kelas siswa ini
$leaderboard = Leaderboard::with('siswa')
->where('id_kelas', $siswa->id_kelas)
->where('semester', $semester)
@@ -27,20 +27,33 @@ public function index()
->orderBy('total_exp', 'desc')
->get()
->map(function ($item, $i) {
+ $fotoProfil = optional($item->siswa)->foto_profil;
return [
- 'ranking' => $i + 1,
- 'nama' => optional($item->siswa)->nama ?? '-',
- 'nisn' => optional($item->siswa)->nisn ?? '-',
- 'exp' => $item->total_exp,
- 'id_siswa'=> $item->id_siswa,
+ 'ranking' => $i + 1,
+ 'nama' => optional($item->siswa)->nama ?? '-',
+ 'nisn' => optional($item->siswa)->nisn ?? '-',
+ 'exp' => $item->total_exp,
+ 'id_siswa' => $item->id_siswa,
+ 'foto_profil' => $fotoProfil,
+ 'foto_url' => $fotoProfil ? Storage::url($fotoProfil) : null,
];
});
- // Posisi siswa yang login
$myRank = $leaderboard->firstWhere('id_siswa', $siswa->id_siswa);
- return view('siswa.leaderboard.index', compact(
- 'leaderboard', 'myRank', 'semester', 'tahunAjaran'
- ));
+ return compact('leaderboard', 'myRank', 'semester', 'tahunAjaran');
+ }
+
+ public function index()
+ {
+ $data = $this->getData();
+ return view('siswa.leaderboard.index', $data);
+ }
+
+ // Endpoint JSON untuk polling real-time
+ public function json()
+ {
+ $data = $this->getData();
+ 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 39b4699..eeadbaa 100644
--- a/app/Http/Controllers/Siswa/ProfileController.php
+++ b/app/Http/Controllers/Siswa/ProfileController.php
@@ -10,13 +10,7 @@
class ProfileController extends Controller
{
- public function edit()
- {
- $siswa = Auth::guard('siswa')->user();
- return view('siswa.profile.edit', compact('siswa'));
- }
-
- public function update(Request $request)
+ public function updateAjax(Request $request)
{
$siswa = Auth::guard('siswa')->user();
@@ -31,28 +25,30 @@ public function update(Request $request)
'foto_profil.max' => 'Ukuran foto maksimal 2MB.',
]);
- $data = [];
+ $data = [];
+ $fotoUrl = null;
- // Update password
if ($request->filled('password')) {
$data['password'] = Hash::make($request->password);
}
- // Update foto profil
if ($request->hasFile('foto_profil')) {
- // Hapus foto lama jika ada
if ($siswa->foto_profil && Storage::disk('public')->exists($siswa->foto_profil)) {
Storage::disk('public')->delete($siswa->foto_profil);
}
$path = $request->file('foto_profil')->store('foto_profil/siswa', 'public');
$data['foto_profil'] = $path;
+ $fotoUrl = Storage::url($path);
}
if (!empty($data)) {
$siswa->update($data);
}
- return redirect()->route('siswa.profile.edit')
- ->with('success', 'Profil berhasil diperbarui!');
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Profil berhasil diperbarui!',
+ 'foto_url' => $fotoUrl ?? ($siswa->foto_profil ? Storage::url($siswa->foto_profil) : null),
+ ]);
}
}
\ No newline at end of file
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php
index d9e9b2f..423dcb3 100644
--- a/resources/views/admin/dashboard.blade.php
+++ b/resources/views/admin/dashboard.blade.php
@@ -2,28 +2,270 @@
@section('title', 'Dashboard Admin')
+@push('styles')
+
+@endpush
+
@section('content')
-
-
Dashboard Admin
-
-
+@php
+ use Carbon\Carbon;
+ $greeting = Carbon::now()->hour < 12 ? 'Selamat Pagi' : (Carbon::now()->hour < 17 ? 'Selamat Siang' : 'Selamat Malam');
+@endphp
-
+
+
+{{-- STAT CARDS --}}
+
+
+
👨🏫
+
Total Guru
+
{{ $totalGuru }}
+
+
+
🏫
+
Total Kelas
+
{{ $totalKelas }}
+
+
+
👨🎓
+
Total Siswa
+
{{ $totalSiswa }}
+
+
+
📚
+
Total Mapel
+
{{ $totalMapel }}
+
+{{-- BOTTOM: Chart + Challenge --}}
+
+
+ {{-- Bar Chart --}}
+
+
+
Total Siswa Berdasarkan Kelas
+
{{ $chartData->count() }} Kelas
+
+
+
+
+
+
+ {{-- Challenge Terbaru --}}
+
+
+
+ @forelse($latestChallenges as $ch)
+
+
+ {{ $ch->kelas->pluck('nama_kelas')->join(', ') ?: 'Semua Kelas' }}
+
+
{{ $ch->judul_challenge }}
+
+ {{ $ch->soal->count() }} Soal
+ @if($ch->tenggat_waktu)
+ · Tenggat: {{ \Carbon\Carbon::parse($ch->tenggat_waktu)->isoFormat('D MMM Y') }}
+ @endif
+
+
+ @empty
+
+ Belum ada challenge dibuat.
+
+ @endforelse
+
+
+ + Tambahkan Challenge Baru
+
+
+
+
+
@endsection
+
+@push('scripts')
+
+
+@endpush
\ No newline at end of file
diff --git a/resources/views/admin/layouts/app.blade.php b/resources/views/admin/layouts/app.blade.php
index f4f9813..b0d8469 100644
--- a/resources/views/admin/layouts/app.blade.php
+++ b/resources/views/admin/layouts/app.blade.php
@@ -3,44 +3,109 @@
+
@yield('title', 'Admin Panel')
@stack('styles')
@@ -48,7 +113,7 @@
-
+{{-- PROFILE MODAL --}}
+
+
+
+
Edit Profil
+
Perbarui foto, username, dan password
+
+
+
+
+
+
@stack('scripts')
\ No newline at end of file
diff --git a/resources/views/guru/dashboard.blade.php b/resources/views/guru/dashboard.blade.php
index f79f39a..9606266 100644
--- a/resources/views/guru/dashboard.blade.php
+++ b/resources/views/guru/dashboard.blade.php
@@ -2,47 +2,192 @@
@section('title', 'Dashboard Guru')
+@push('styles')
+
+@endpush
+
@section('content')
-
-
Dashboard Guru
+@php
+ use Carbon\Carbon;
+ use App\Models\Mengajar;
+ $guru = Auth::guard('guru')->user();
+ $greeting = Carbon::now()->hour < 12 ? 'Selamat Pagi' : (Carbon::now()->hour < 17 ? 'Selamat Siang' : 'Selamat Malam');
-
+ // Ambil mengajar dengan relasi mapel & kelas
+ $mengajars = Mengajar::with(['mapel', 'kelas'])
+ ->where('id_guru', $guru->id_guru)
+ ->get();
+@endphp
-
-
-
-
Total Kelas Diampu
-
- {{ $totalKelas }}
-
-
-
+
+
+{{-- STAT CARDS --}}
+
+
+
🏫
+
Kelas Diampu
+
{{ $totalKelas }}
+
+
+
📚
+
Mata Pelajaran
+
{{ $totalMapel }}
+
+
+
👨🎓
+
Siswa Diajar
+
{{ $totalSiswa }}
+
+
+
+{{-- INFO GRID --}}
+
+
+ {{-- Daftar Mengajar --}}
+
+
-
-
-
-
Total Mata Pelajaran
-
- {{ $totalMapel }}
-
-
-
+ @php $dotColors = ['#2b8ef3','#22c55e','#f97316','#a855f7','#ec4899','#eab308']; @endphp
+
+ @forelse($mengajars->take(6) as $i => $m)
+
+
+
{{ optional($m->mapel)->nama_mapel ?? '-' }}
+
{{ optional($m->kelas)->nama_kelas ?? '-' }}
+
+ @empty
+
+ Belum ada data mengajar.
+
+ @endforelse
+
+
+ {{-- Quick Links --}}
+
-@endsection
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/guru/layouts/app.blade.php b/resources/views/guru/layouts/app.blade.php
index e21e04c..6ad500b 100644
--- a/resources/views/guru/layouts/app.blade.php
+++ b/resources/views/guru/layouts/app.blade.php
@@ -3,41 +3,81 @@
+
@yield('title', 'Panel Guru')
@stack('styles')
@@ -45,10 +85,8 @@
-
+
+{{-- PROFILE MODAL --}}
+
+
+
+
Edit Profil
+
Perbarui foto profil dan password
+
+
+
+
+
+
@stack('scripts')
\ 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 8c8e621..2e5f848 100644
--- a/resources/views/siswa/layouts/app.blade.php
+++ b/resources/views/siswa/layouts/app.blade.php
@@ -3,153 +3,88 @@
+
@yield('title', 'Siswa Panel')
@stack('styles')
@@ -162,70 +97,52 @@
-
-
-
+{{-- PROFILE MODAL --}}
+
+
+
+
+
+
Edit Profil
+
Perbarui foto profil dan password
+
+
+
+
+
+
+
@stack('scripts')
diff --git a/resources/views/siswa/leaderboard/index.blade.php b/resources/views/siswa/leaderboard/index.blade.php
index 80e330e..7fbc90c 100644
--- a/resources/views/siswa/leaderboard/index.blade.php
+++ b/resources/views/siswa/leaderboard/index.blade.php
@@ -10,16 +10,9 @@
.podium-wrap { display: flex; align-items: flex-end; justify-content: center; gap: 12px; margin-bottom: 32px; }
.podium-item { display: flex; flex-direction: column; align-items: center; gap: 8px; }
-.podium-avatar {
- border-radius: 50%;
- display: flex; align-items: center; justify-content: center;
- font-size: 22px; font-weight: 800; color: white;
- position: relative; overflow: hidden; flex-shrink: 0;
- width: 56px; height: 56px;
-}
-
+.podium-avatar { border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 800; color: white; position: relative; overflow: hidden; flex-shrink: 0; width: 56px; height: 56px; }
.podium-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
-.podium-crown { position: absolute; top: -16px; font-size: 18px; z-index: 1; }
+.podium-crown { position: absolute; top: -16px; font-size: 18px; z-index: 1; pointer-events: none; }
.rank-1 .podium-avatar { background: linear-gradient(135deg,#f59e0b,#d97706); width:68px; height:68px; font-size:26px; }
.rank-2 .podium-avatar { background: linear-gradient(135deg,#94a3b8,#64748b); }
@@ -36,14 +29,17 @@
.my-rank-banner { background:linear-gradient(135deg,#667eea,#764ba2); border-radius:16px; padding:16px 20px; color:white; display:flex; align-items:center; gap:16px; margin-bottom:20px; }
.my-rank-avatar { width:48px; height:48px; border-radius:50%; object-fit:cover; border:2px solid rgba(255,255,255,0.5); flex-shrink:0; }
.my-rank-avatar-placeholder { width:48px; height:48px; border-radius:50%; background:rgba(255,255,255,0.2); display:flex; align-items:center; justify-content:center; font-size:20px; font-weight:800; flex-shrink:0; }
-.my-rank-num { font-size:36px; font-weight:800; line-height:1; }
-.my-rank-info { flex:1; }
+.my-rank-num { font-size:36px; font-weight:800; line-height:1; }
+.my-rank-info { flex:1; }
.my-rank-label { font-size:12px; opacity:0.8; margin-bottom:2px; }
.my-rank-nama { font-size:16px; font-weight:700; }
.my-rank-exp { font-size:13px; opacity:0.9; }
.custom-card { background:white; border-radius:20px; border:2px solid #e5e5e5; padding:22px; }
-.section-title { font-size:15px; font-weight:700; color:#1e293b; margin-bottom:16px; }
+.section-title { font-size:15px; font-weight:700; color:#1e293b; margin-bottom:16px; display:flex; align-items:center; justify-content:space-between; }
+
+.live-dot { width:8px; height:8px; border-radius:50%; background:#22c55e; box-shadow:0 0 6px #22c55e; animation: pulse 2s ease-in-out infinite; display:inline-block; margin-right:6px; }
+@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.5;transform:scale(0.8)} }
.lb-row { display:flex; align-items:center; gap:12px; padding:10px 14px; border-radius:12px; margin-bottom:8px; transition:background 0.15s; }
.lb-row:hover { background:#f8fafc; }
@@ -75,125 +71,172 @@
Semester {{ $semester }} · {{ $tahunAjaran }}
-@if($leaderboard->isEmpty())
-
-
📊
-
Belum ada data leaderboard.
-
Kerjakan challenge untuk masuk leaderboard!
+{{-- Container di-render real-time via JavaScript --}}
+
- @php
- $top3 = $leaderboard->take(3);
- $first = $top3->firstWhere('ranking', 1);
- $second = $top3->firstWhere('ranking', 2);
- $third = $top3->firstWhere('ranking', 3);
- @endphp
+@endsection
- @if($first)
-
+@push('scripts')
+
+@endpush
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index 3fb21d2..da598b6 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -88,8 +88,8 @@
return view('admin.notif');
})->name('notif');
- Route::get('/profile', [AdminProfileController::class, 'edit'])->name('profile.edit');
- Route::put('/profile', [AdminProfileController::class, 'update'])->name('profile.update');
+ Route::get('/profile', [AdminProfileController::class, 'edit'])->name('profile.edit');
+ Route::post('/profile', [AdminProfileController::class, 'updateAjax'])->name('profile.update');
// ── GURU ──────────────────────────────────────────────
Route::get('/guru/kelas-by-mapel', [AdminGuruController::class, 'getKelasByMapel'])
@@ -154,8 +154,8 @@
Route::get('/tugas/{id}/detail', [GuruMapelController::class, 'detailTugas'])->name('tugas.detail');
Route::delete('/tugas/{id}', [GuruMapelController::class, 'destroyTugas'])->name('tugas.destroy');
- Route::get('/profile', [GuruProfileController::class, 'edit'])->name('profile.edit');
- Route::put('/profile', [GuruProfileController::class, 'update'])->name('profile.update');
+ Route::get('/profile', [GuruProfileController::class, 'edit'])->name('profile.edit');
+ Route::post('/profile', [GuruProfileController::class, 'updateAjax'])->name('profile.update');
// LOGOUT GURU
Route::post('/logout', [GuruLoginController::class, 'logout'])->name('logout');
@@ -185,10 +185,11 @@
//LEADERBOARD SISWA
Route::get('/leaderboard', [SiswaLeaderboardController::class, 'index'])->name('leaderboard.index');
+ Route::get('/leaderboard/json', [SiswaLeaderboardController::class, 'json'])->name('leaderboard.json');
//PROFILE SISWA
- Route::get('/profile', [SiswaProfileController::class, 'edit'])->name('profile.edit');
- Route::put('/profile', [SiswaProfileController::class, 'update'])->name('profile.update');
+ Route::get('/profile', [SiswaProfileController::class, 'edit'])->name('profile.edit');
+ Route::post('/profile', [SiswaProfileController::class, 'updateAjax'])->name('profile.update');
// LOGOUT SISWA
Route::post('/logout', [SiswaLoginController::class, 'logout'])->name('logout');