diff --git a/app/Http/Controllers/Admin/ProfileController.php b/app/Http/Controllers/Admin/ProfileController.php
new file mode 100644
index 0000000..87e7a3a
--- /dev/null
+++ b/app/Http/Controllers/Admin/ProfileController.php
@@ -0,0 +1,57 @@
+user();
+ return view('admin.profile.edit', compact('admin'));
+ }
+
+ public function update(Request $request)
+ {
+ $admin = Auth::guard('admin')->user();
+
+ $request->validate([
+ 'username' => 'required|string|max:100|unique:admins,username,' . $admin->id_admin . ',id_admin',
+ 'password' => 'nullable|min:6|confirmed',
+ 'password_confirmation' => 'nullable',
+ 'foto_profil' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
+ ], [
+ 'username.required' => 'Username wajib diisi.',
+ 'username.unique' => 'Username sudah digunakan.',
+ 'password.min' => 'Password minimal 6 karakter.',
+ 'password.confirmed' => 'Konfirmasi password tidak cocok.',
+ 'foto_profil.image' => 'File harus berupa gambar.',
+ 'foto_profil.max' => 'Ukuran foto maksimal 2MB.',
+ ]);
+
+ $data = ['username' => $request->username];
+
+ if ($request->filled('password')) {
+ $data['password'] = Hash::make($request->password);
+ }
+
+ 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;
+ }
+
+ $admin->update($data);
+
+ return redirect()->route('admin.profile.edit')
+ ->with('success', 'Profil berhasil diperbarui!');
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Guru/ProfilController.php b/app/Http/Controllers/Guru/ProfilController.php
deleted file mode 100644
index b9ce187..0000000
--- a/app/Http/Controllers/Guru/ProfilController.php
+++ /dev/null
@@ -1,42 +0,0 @@
-user();
- return view('guru.profil.show', compact('guru'));
- }
-
- public function update(Request $request)
- {
- $guru = Auth::guard('guru')->user();
-
- $validated = $request->validate([
- 'nama' => 'required|string|max:100',
- 'password' => 'nullable|string|min:6|confirmed',
- ], [
- 'nama.required' => 'Nama wajib diisi',
- 'password.min' => 'Password minimal 6 karakter',
- 'password.confirmed' => 'Konfirmasi password tidak cocok',
- ]);
-
- $guru->nama = $validated['nama'];
-
- if ($request->filled('password')) {
- $guru->password = Hash::make($validated['password']);
- }
-
- $guru->save();
-
- return redirect()->route('guru.profil.show')
- ->with('success', 'Profil berhasil diupdate!');
- }
-}
\ No newline at end of file
diff --git a/app/Http/Controllers/Guru/ProfileController.php b/app/Http/Controllers/Guru/ProfileController.php
new file mode 100644
index 0000000..31fb263
--- /dev/null
+++ b/app/Http/Controllers/Guru/ProfileController.php
@@ -0,0 +1,55 @@
+user();
+ return view('guru.profile.edit', compact('guru'));
+ }
+
+ public function update(Request $request)
+ {
+ $guru = Auth::guard('guru')->user();
+
+ $request->validate([
+ 'password' => 'nullable|min:6|confirmed',
+ 'password_confirmation' => 'nullable',
+ 'foto_profil' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
+ ], [
+ 'password.min' => 'Password minimal 6 karakter.',
+ 'password.confirmed' => 'Konfirmasi password tidak cocok.',
+ 'foto_profil.image' => 'File harus berupa gambar.',
+ 'foto_profil.max' => 'Ukuran foto maksimal 2MB.',
+ ]);
+
+ $data = [];
+
+ if ($request->filled('password')) {
+ $data['password'] = Hash::make($request->password);
+ }
+
+ if ($request->hasFile('foto_profil')) {
+ if ($guru->foto_profil && Storage::disk('public')->exists($guru->foto_profil)) {
+ Storage::disk('public')->delete($guru->foto_profil);
+ }
+ $path = $request->file('foto_profil')->store('foto_profil/guru', 'public');
+ $data['foto_profil'] = $path;
+ }
+
+ if (!empty($data)) {
+ $guru->update($data);
+ }
+
+ return redirect()->route('guru.profile.edit')
+ ->with('success', 'Profil berhasil diperbarui!');
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
deleted file mode 100644
index a48eb8d..0000000
--- a/app/Http/Controllers/ProfileController.php
+++ /dev/null
@@ -1,60 +0,0 @@
- $request->user(),
- ]);
- }
-
- /**
- * Update the user's profile information.
- */
- public function update(ProfileUpdateRequest $request): RedirectResponse
- {
- $request->user()->fill($request->validated());
-
- if ($request->user()->isDirty('email')) {
- $request->user()->email_verified_at = null;
- }
-
- $request->user()->save();
-
- return Redirect::route('profile.edit')->with('status', 'profile-updated');
- }
-
- /**
- * Delete the user's account.
- */
- public function destroy(Request $request): RedirectResponse
- {
- $request->validateWithBag('userDeletion', [
- 'password' => ['required', 'current_password'],
- ]);
-
- $user = $request->user();
-
- Auth::logout();
-
- $user->delete();
-
- $request->session()->invalidate();
- $request->session()->regenerateToken();
-
- return Redirect::to('/');
- }
-}
diff --git a/app/Http/Controllers/Siswa/ProfileController.php b/app/Http/Controllers/Siswa/ProfileController.php
new file mode 100644
index 0000000..39b4699
--- /dev/null
+++ b/app/Http/Controllers/Siswa/ProfileController.php
@@ -0,0 +1,58 @@
+user();
+ return view('siswa.profile.edit', compact('siswa'));
+ }
+
+ public function update(Request $request)
+ {
+ $siswa = Auth::guard('siswa')->user();
+
+ $request->validate([
+ 'password' => 'nullable|min:6|confirmed',
+ 'password_confirmation' => 'nullable',
+ 'foto_profil' => 'nullable|image|mimes:jpg,jpeg,png,webp|max:2048',
+ ], [
+ 'password.min' => 'Password minimal 6 karakter.',
+ 'password.confirmed' => 'Konfirmasi password tidak cocok.',
+ 'foto_profil.image' => 'File harus berupa gambar.',
+ 'foto_profil.max' => 'Ukuran foto maksimal 2MB.',
+ ]);
+
+ $data = [];
+
+ // 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;
+ }
+
+ if (!empty($data)) {
+ $siswa->update($data);
+ }
+
+ return redirect()->route('siswa.profile.edit')
+ ->with('success', 'Profil berhasil diperbarui!');
+ }
+}
\ No newline at end of file
diff --git a/app/Models/Admin.php b/app/Models/Admin.php
index 2b9d766..862ad7a 100644
--- a/app/Models/Admin.php
+++ b/app/Models/Admin.php
@@ -19,7 +19,8 @@ class Admin extends Authenticatable
protected $fillable = [
'username',
- 'password'
+ 'password',
+ 'foto_profil'
];
protected $hidden = [
diff --git a/app/Models/Guru.php b/app/Models/Guru.php
index 5b35a97..91af307 100644
--- a/app/Models/Guru.php
+++ b/app/Models/Guru.php
@@ -17,7 +17,8 @@ class Guru extends Authenticatable
protected $fillable = [
'nip',
'nama',
- 'password'
+ 'password',
+ 'foto_profil'
];
protected $hidden = [
diff --git a/app/Models/Siswa.php b/app/Models/Siswa.php
index 39d03d6..c8ac042 100644
--- a/app/Models/Siswa.php
+++ b/app/Models/Siswa.php
@@ -21,6 +21,7 @@ class Siswa extends Authenticatable
'tanggal_lahir',
'id_kelas',
'password',
+ 'foto_profil'
];
protected $hidden = [
diff --git a/database/migrations/2026_03_14_024528_add_pp.php b/database/migrations/2026_03_14_024528_add_pp.php
new file mode 100644
index 0000000..3b2e58b
--- /dev/null
+++ b/database/migrations/2026_03_14_024528_add_pp.php
@@ -0,0 +1,42 @@
+string('foto_profil')->nullable()->after('password');
+ });
+ }
+
+ if (!Schema::hasColumn('gurus', 'foto_profil')) {
+ Schema::table('gurus', function (Blueprint $table) {
+ $table->string('foto_profil')->nullable()->after('password');
+ });
+ }
+
+ if (!Schema::hasColumn('admins', 'foto_profil')) {
+ Schema::table('admins', function (Blueprint $table) {
+ $table->string('foto_profil')->nullable()->after('password');
+ });
+ }
+ }
+
+ public function down(): void
+ {
+ Schema::table('siswas', function (Blueprint $table) {
+ $table->dropColumn('foto_profil');
+ });
+ Schema::table('gurus', function (Blueprint $table) {
+ $table->dropColumn('foto_profil');
+ });
+ Schema::table('admins', function (Blueprint $table) {
+ $table->dropColumn('foto_profil');
+ });
+ }
+};
\ 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 8e80979..f4f9813 100644
--- a/resources/views/admin/layouts/app.blade.php
+++ b/resources/views/admin/layouts/app.blade.php
@@ -9,92 +9,42 @@
@stack('styles')
-
@@ -104,67 +54,39 @@
+
+@endsection
+
+@push('scripts')
+
+@endpush
\ 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 bc880ac..e21e04c 100644
--- a/resources/views/guru/layouts/app.blade.php
+++ b/resources/views/guru/layouts/app.blade.php
@@ -9,77 +9,39 @@
@stack('styles')
-
@@ -88,40 +50,23 @@
-
-
-
-
-
-
-
-
-
-
-
+
+@endsection
+
+@push('scripts')
+
+@endpush
\ No newline at end of file
diff --git a/resources/views/guru/profil/index.blade.php b/resources/views/guru/profile/index.blade.php
similarity index 100%
rename from resources/views/guru/profil/index.blade.php
rename to resources/views/guru/profile/index.blade.php
diff --git a/resources/views/siswa/layouts/app.blade.php b/resources/views/siswa/layouts/app.blade.php
index 4866b81..8c8e621 100644
--- a/resources/views/siswa/layouts/app.blade.php
+++ b/resources/views/siswa/layouts/app.blade.php
@@ -35,28 +35,16 @@
position: relative;
}
- /* Default: collapsed saat pertama load */
.sidebar.collapsed {
width: 0;
padding: 0;
border-right: none;
}
- .sidebar-logo {
- text-align: center;
- margin-bottom: 40px;
- white-space: nowrap;
- }
+ .sidebar-logo { text-align: center; margin-bottom: 40px; white-space: nowrap; }
+ .sidebar-logo img { width: 90px; }
- .sidebar-logo img {
- width: 90px;
- }
-
- .sidebar-menu {
- display: flex;
- flex-direction: column;
- white-space: nowrap;
- }
+ .sidebar-menu { display: flex; flex-direction: column; white-space: nowrap; }
.sidebar-link {
display: flex;
@@ -71,42 +59,19 @@
transition: all 0.2s ease;
}
- .sidebar-link:hover {
- background: #e6f0ff;
- color: #1d4ed8;
- }
+ .sidebar-link:hover { background: #e6f0ff; color: #1d4ed8; }
+ .sidebar-link.active { background: #e6f0ff; color: #1d4ed8; }
- .sidebar-link.active {
- background: #e6f0ff;
- color: #1d4ed8;
- }
+ .sidebar-icon { width: 20px; height: 20px; flex-shrink: 0; }
+ .sidebar-logout { margin-top: auto; }
+ .sidebar-logout button { width: 100%; border: none; background: transparent; color: #ef4444; font-weight: 600; padding: 10px; text-align: left; }
- .sidebar-icon {
- width: 20px;
- height: 20px;
- flex-shrink: 0;
- }
-
- .sidebar-logout {
- margin-top: auto;
- }
-
- .sidebar-logout button {
- width: 100%;
- border: none;
- background: transparent;
- color: #ef4444;
- font-weight: 600;
- padding: 10px;
- text-align: left;
- }
-
- /* ===== TOGGLE ARROW BUTTON ===== */
+ /* ===== TOGGLE BUTTON ===== */
.sidebar-toggle-btn {
position: fixed;
top: 50%;
transform: translateY(-50%);
- left: 260px; /* sama dengan lebar sidebar saat terbuka */
+ left: 260px;
z-index: 1000;
width: 22px;
height: 48px;
@@ -121,19 +86,11 @@
box-shadow: 2px 0 8px rgba(43, 142, 243, 0.3);
}
- .sidebar-toggle-btn:hover {
- background: #1a7ae0;
- }
+ .sidebar-toggle-btn:hover { background: #1a7ae0; }
+ .sidebar-toggle-btn.collapsed { left: 0; }
- /* Posisi tombol saat sidebar collapsed */
- .sidebar-toggle-btn.collapsed {
- left: 0;
- }
-
- /* Arrow icon SVG */
.toggle-arrow {
- width: 12px;
- height: 12px;
+ width: 12px; height: 12px;
fill: none;
stroke: white;
stroke-width: 2.5;
@@ -142,20 +99,10 @@
transition: transform 0.3s ease;
}
- /* Saat collapsed, panah mengarah ke kanan (buka) */
- .sidebar-toggle-btn.collapsed .toggle-arrow {
- transform: rotate(180deg);
- }
+ .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 */
- }
+ .main { flex: 1; background: #f5f9ff; display: flex; flex-direction: column; transition: all 0.3s ease; min-width: 0; }
/* TOPBAR */
.topbar {
@@ -169,31 +116,40 @@
align-items: center;
}
- .topbar-left {
- font-weight: 600;
- font-size: 16px;
- display: flex;
- align-items: center;
- gap: 10px;
- }
+ .topbar-left { font-weight: 600; font-size: 16px; display: flex; align-items: center; gap: 10px; }
+ .topbar-right { display: flex; align-items: center; gap: 14px; }
+ .topbar-icon { width: 24px; height: 24px; cursor: pointer; }
- .topbar-right {
- display: flex;
- align-items: center;
- gap: 16px;
- }
-
- .topbar-icon {
- width: 24px;
- height: 24px;
+ /* Foto profil di topbar */
+ .topbar-avatar {
+ width: 36px; height: 36px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 2px solid rgba(255,255,255,0.5);
cursor: pointer;
+ transition: border-color 0.2s;
+ display: block;
}
- /* CONTENT */
- .content {
- padding: 20px 30px;
- flex: 1;
+ .topbar-avatar:hover { border-color: white; }
+
+ .topbar-avatar-icon {
+ width: 36px; height: 36px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.2);
+ border: 2px solid rgba(255,255,255,0.4);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: background 0.2s;
+ flex-shrink: 0;
}
+
+ .topbar-avatar-icon:hover { background: rgba(255,255,255,0.3); }
+ .topbar-avatar-icon svg { width: 20px; height: 20px; stroke: white; }
+
+ .content { padding: 20px 30px; flex: 1; }
@stack('styles')
@@ -202,38 +158,32 @@
-
-
-
-
@stack('scripts')
diff --git a/resources/views/siswa/leaderboard/index.blade.php b/resources/views/siswa/leaderboard/index.blade.php
index eada5fd..80e330e 100644
--- a/resources/views/siswa/leaderboard/index.blade.php
+++ b/resources/views/siswa/leaderboard/index.blade.php
@@ -7,137 +7,62 @@
.page-title { font-size: 28px; font-weight: 800; margin-top: -20px; margin-bottom: 6px; }
.page-subtitle { font-size: 14px; color: #64748b; margin-bottom: 24px; }
-/* Podium top 3 */
-.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-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 {
- width: 56px;
- height: 56px;
border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- font-weight: 800;
- color: white;
- position: relative;
+ 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-crown {
- position: absolute;
- top: -16px;
- font-size: 18px;
-}
+.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; }
-.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); }
-.rank-3 .podium-avatar { background: linear-gradient(135deg, #f97316, #ea580c); }
+.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); }
+.rank-3 .podium-avatar { background: linear-gradient(135deg,#f97316,#ea580c); }
-.podium-name { font-size: 13px; font-weight: 700; color: #1e293b; text-align: center; max-width: 90px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-.podium-exp { font-size: 12px; color: #64748b; }
+.podium-name { font-size:13px; font-weight:700; color:#1e293b; text-align:center; max-width:90px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
+.podium-exp { font-size:12px; color:#64748b; }
-.podium-bar {
- border-radius: 12px 12px 0 0;
- width: 80px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 20px;
- font-weight: 800;
- color: white;
-}
+.podium-bar { border-radius:12px 12px 0 0; width:80px; display:flex; align-items:center; justify-content:center; font-size:20px; font-weight:800; color:white; }
+.rank-1 .podium-bar { height:80px; background:linear-gradient(135deg,#f59e0b,#d97706); }
+.rank-2 .podium-bar { height:60px; background:linear-gradient(135deg,#94a3b8,#64748b); }
+.rank-3 .podium-bar { height:44px; background:linear-gradient(135deg,#f97316,#ea580c); }
-.rank-1 .podium-bar { height: 80px; background: linear-gradient(135deg, #f59e0b, #d97706); }
-.rank-2 .podium-bar { height: 60px; background: linear-gradient(135deg, #94a3b8, #64748b); }
-.rank-3 .podium-bar { height: 44px; background: linear-gradient(135deg, #f97316, #ea580c); }
+.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-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; }
-/* My rank banner */
-.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;
-}
+.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; }
-.my-rank-num {
- font-size: 36px;
- font-weight: 800;
- line-height: 1;
-}
+.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; }
+.lb-row.highlight { background:#f0eeff; border:2px solid #c4b5fd; }
-.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; }
+.lb-rank { width:32px; height:32px; border-radius:50%; background:#e2e8f0; display:flex; align-items:center; justify-content:center; font-size:13px; font-weight:700; color:#64748b; flex-shrink:0; }
+.lb-rank.gold { background:#fef3c7; color:#d97706; }
+.lb-rank.silver { background:#f1f5f9; color:#64748b; }
+.lb-rank.bronze { background:#ffedd5; color:#ea580c; }
-/* Tabel */
-.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; }
+.lb-avatar { width:36px; height:36px; border-radius:50%; object-fit:cover; flex-shrink:0; border:2px solid #e2e8f0; }
+.lb-avatar-placeholder { width:36px; height:36px; border-radius:50%; background:#e6f0ff; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:700; color:#2b8ef3; flex-shrink:0; }
-.lb-row {
- display: flex;
- align-items: center;
- gap: 14px;
- padding: 12px 14px;
- border-radius: 12px;
- margin-bottom: 8px;
- transition: background 0.15s;
-}
+.lb-nama { flex:1; font-size:14px; font-weight:600; color:#1e293b; }
+.lb-nisn { font-size:12px; color:#94a3b8; }
+.lb-exp { font-size:14px; font-weight:700; color:#667eea; }
-.lb-row:hover { background: #f8fafc; }
-.lb-row.highlight { background: #f0eeff; border: 2px solid #c4b5fd; }
-
-.lb-rank {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- background: #e2e8f0;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 13px;
- font-weight: 700;
- color: #64748b;
- flex-shrink: 0;
-}
-
-.lb-rank.gold { background: #fef3c7; color: #d97706; }
-.lb-rank.silver { background: #f1f5f9; color: #64748b; }
-.lb-rank.bronze { background: #ffedd5; color: #ea580c; }
-
-.lb-nama { flex: 1; font-size: 14px; font-weight: 600; color: #1e293b; }
-.lb-nisn { font-size: 12px; color: #94a3b8; }
-.lb-exp { font-size: 14px; font-weight: 700; color: #667eea; }
-
-.semester-badge {
- display: inline-block;
- background: #f0eeff;
- color: #667eea;
- font-size: 12px;
- font-weight: 700;
- padding: 4px 12px;
- border-radius: 99px;
- margin-bottom: 20px;
-}
-
-.empty-state { text-align: center; padding: 40px 20px; color: #94a3b8; }
+.semester-badge { display:inline-block; background:#f0eeff; color:#667eea; font-size:12px; font-weight:700; padding:4px 12px; border-radius:99px; margin-bottom:20px; }
+.empty-state { text-align:center; padding:40px 20px; color:#94a3b8; }
@endpush
@@ -148,9 +73,7 @@
๐
Leaderboard
Peringkat siswa berdasarkan total EXP yang dikumpulkan.
-
- Semester {{ $semester }} ยท {{ $tahunAjaran }}
-
+
Semester {{ $semester }} ยท {{ $tahunAjaran }}
@if($leaderboard->isEmpty())
@@ -160,9 +83,8 @@
@else
- {{-- Podium Top 3 --}}
@php
- $top3 = $leaderboard->take(3);
+ $top3 = $leaderboard->take(3);
$first = $top3->firstWhere('ranking', 1);
$second = $top3->firstWhere('ranking', 2);
$third = $top3->firstWhere('ranking', 3);
@@ -171,31 +93,44 @@
@if($first)
- {{-- Rank 2 --}}
@if($second)
-
{{ strtoupper(substr($second['nama'], 0, 1)) }}
+
+ @if(!empty($second['foto_profil']))
+
 }})
+ @else
+ {{ strtoupper(substr($second['nama'], 0, 1)) }}
+ @endif
+
{{ $second['nama'] }}
โญ {{ number_format($second['exp']) }}
2
@endif
- {{-- Rank 1 --}}
๐
- {{ strtoupper(substr($first['nama'], 0, 1)) }}
+ @if(!empty($first['foto_profil']))
+
 }})
+ @else
+ {{ strtoupper(substr($first['nama'], 0, 1)) }}
+ @endif
{{ $first['nama'] }}
โญ {{ number_format($first['exp']) }}
1
- {{-- Rank 3 --}}
@if($third)
-
{{ strtoupper(substr($third['nama'], 0, 1)) }}
+
+ @if(!empty($third['foto_profil']))
+
 }})
+ @else
+ {{ strtoupper(substr($third['nama'], 0, 1)) }}
+ @endif
+
{{ $third['nama'] }}
โญ {{ number_format($third['exp']) }}
3
@@ -205,9 +140,13 @@
@endif
- {{-- My Rank Banner --}}
@if($myRank)
+ @if(!empty($myRank['foto_profil']))
+
 }})
+ @else
+
{{ strtoupper(substr($myRank['nama'], 0, 1)) }}
+ @endif
#{{ $myRank['ranking'] }}
Posisimu saat ini
@@ -218,16 +157,13 @@
@endif
- {{-- Tabel semua --}}
๐ Semua Peringkat
@foreach($leaderboard as $item)
@php
$isMe = $item['id_siswa'] === $siswaLogin->id_siswa;
- $rankClass = match($item['ranking']) {
- 1 => 'gold', 2 => 'silver', 3 => 'bronze', default => ''
- };
+ $rankClass = match($item['ranking']) { 1=>'gold', 2=>'silver', 3=>'bronze', default=>'' };
@endphp
@@ -237,10 +173,19 @@
@else {{ $item['ranking'] }}
@endif
+
+ @if(!empty($item['foto_profil']))
+
 }})
+ @else
+
{{ strtoupper(substr($item['nama'], 0, 1)) }}
+ @endif
+
{{ $item['nama'] }}
- @if($isMe) Kamu @endif
+ @if($isMe)
+ Kamu
+ @endif
{{ $item['nisn'] }}
diff --git a/resources/views/siswa/profile/edit.blade.php b/resources/views/siswa/profile/edit.blade.php
new file mode 100644
index 0000000..642f465
--- /dev/null
+++ b/resources/views/siswa/profile/edit.blade.php
@@ -0,0 +1,255 @@
+@extends('siswa.layouts.app')
+
+@section('title', 'Edit Profil')
+
+@push('styles')
+
+@endpush
+
+@section('content')
+
+@php $siswa = Auth::guard('siswa')->user(); @endphp
+
+
Edit Profil
+
Perbarui foto profil dan password akunmu.
+
+@if(session('success'))
+
โ {{ session('success') }}
+@endif
+
+@if($errors->any())
+
+
+ @foreach($errors->all() as $error)
+ - {{ $error }}
+ @endforeach
+
+
+@endif
+
+
+
+@endsection
+
+@push('scripts')
+
+@endpush
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index bb3aa00..3fb21d2 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -13,16 +13,16 @@
use App\Http\Controllers\Admin\MapelController as AdminMapelController;
use App\Http\Controllers\Admin\ChallengeController as AdminChallengeController;
use App\Http\Controllers\Admin\LeaderboardController as AdminLeaderboardController;
+use App\Http\Controllers\Admin\ProfileController as AdminProfileController;
use App\Http\Controllers\Admin\MateriTugasController as AdminMateriTugasController;
-
// GURU CONTROLLERS
use App\Http\Controllers\Guru\LoginController as GuruLoginController;
use App\Http\Controllers\Guru\DashboardController as GuruDashboardController;
use App\Http\Controllers\Guru\GuruController as GuruGuruController;
use App\Http\Controllers\Guru\KelasController as GuruKelasController;
use App\Http\Controllers\Guru\SiswaController as GuruSiswaController;
-use App\Http\Controllers\Guru\ProfilController as GuruProfilController;
+use App\Http\Controllers\Guru\ProfileController as GuruProfileController;
use App\Http\Controllers\Guru\MapelController as GuruMapelController;
use App\Http\Controllers\Guru\LeaderboardController as GuruLeaderboardController;
@@ -33,6 +33,7 @@
use App\Http\Controllers\Siswa\TugasController as SiswaTugasController;
use App\Http\Controllers\Siswa\ChallengeController as SiswaChallengeController;
use App\Http\Controllers\Siswa\LeaderboardController as SiswaLeaderboardController;
+use App\Http\Controllers\Siswa\ProfileController as SiswaProfileController;
// ====================
// LANDING PAGE
@@ -87,9 +88,8 @@
return view('admin.notif');
})->name('notif');
- Route::get('/profil', function () {
- return view('admin.profil');
- })->name('profil');
+ Route::get('/profile', [AdminProfileController::class, 'edit'])->name('profile.edit');
+ Route::put('/profile', [AdminProfileController::class, 'update'])->name('profile.update');
// โโ GURU โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Route::get('/guru/kelas-by-mapel', [AdminGuruController::class, 'getKelasByMapel'])
@@ -111,7 +111,6 @@
Route::delete('/tugas/{id}', [AdminMateriTugasController::class, 'destroyTugas'])->name('tugas.destroy');
// โโ CHALLENGE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- // WAJIB di atas Route::resource agar tidak konflik dengan {challenge} wildcard
Route::get('/challenge/{id}/edit-data', [AdminChallengeController::class, 'editData'])
->name('challenge.editData');
Route::resource('challenge', AdminChallengeController::class);
@@ -155,10 +154,8 @@
Route::get('/tugas/{id}/detail', [GuruMapelController::class, 'detailTugas'])->name('tugas.detail');
Route::delete('/tugas/{id}', [GuruMapelController::class, 'destroyTugas'])->name('tugas.destroy');
-
- // Profil (Edit)
- Route::get('/profil', [GuruProfilController::class, 'show'])->name('profil.show');
- Route::put('/profil', [GuruProfilController::class, 'update'])->name('profil.update');
+ Route::get('/profile', [GuruProfileController::class, 'edit'])->name('profile.edit');
+ Route::put('/profile', [GuruProfileController::class, 'update'])->name('profile.update');
// LOGOUT GURU
Route::post('/logout', [GuruLoginController::class, 'logout'])->name('logout');
@@ -188,6 +185,10 @@
//LEADERBOARD SISWA
Route::get('/leaderboard', [SiswaLeaderboardController::class, 'index'])->name('leaderboard.index');
+
+ //PROFILE SISWA
+ Route::get('/profile', [SiswaProfileController::class, 'edit'])->name('profile.edit');
+ Route::put('/profile', [SiswaProfileController::class, 'update'])->name('profile.update');
// LOGOUT SISWA
Route::post('/logout', [SiswaLoginController::class, 'logout'])->name('logout');