profile 3 user
This commit is contained in:
parent
3e4c585230
commit
7f81522b85
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\Admin;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function edit()
|
||||
{
|
||||
$admin = Auth::guard('admin')->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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guru;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class ProfilController extends Controller
|
||||
{
|
||||
public function show()
|
||||
{
|
||||
$guru = Auth::guard('guru')->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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guru;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function edit()
|
||||
{
|
||||
$guru = Auth::guard('guru')->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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): View
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $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('/');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Siswa;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function edit()
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,8 @@ class Admin extends Authenticatable
|
|||
|
||||
protected $fillable = [
|
||||
'username',
|
||||
'password'
|
||||
'password',
|
||||
'foto_profil'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ class Guru extends Authenticatable
|
|||
protected $fillable = [
|
||||
'nip',
|
||||
'nama',
|
||||
'password'
|
||||
'password',
|
||||
'foto_profil'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Siswa extends Authenticatable
|
|||
'tanggal_lahir',
|
||||
'id_kelas',
|
||||
'password',
|
||||
'foto_profil'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (!Schema::hasColumn('siswas', 'foto_profil')) {
|
||||
Schema::table('siswas', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -9,92 +9,42 @@
|
|||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body { font-family: 'Poppins', sans-serif; background-color: #f8f9fa; margin: 0; }
|
||||
.admin-wrapper { display: flex; min-height: 100vh; }
|
||||
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
background: #ffffff;
|
||||
border-right: 2px solid #e6f0ff;
|
||||
padding: 30px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar { width: 260px; background: #ffffff; border-right: 2px solid #e6f0ff; padding: 30px 20px; display: flex; flex-direction: column; }
|
||||
.sidebar-logo { text-align: center; margin-bottom: 40px; }
|
||||
.sidebar-logo img { width: 90px; }
|
||||
|
||||
.sidebar-menu { display: flex; flex-direction: column; }
|
||||
|
||||
.sidebar-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 18px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 12px;
|
||||
color: #64748b;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-link { display: flex; align-items: center; gap: 12px; padding: 12px 18px; margin-bottom: 12px; border-radius: 12px; color: #64748b; text-decoration: none; font-weight: 500; transition: all 0.2s ease; }
|
||||
.sidebar-link:hover { background: #e6f0ff; color: #1d4ed8; }
|
||||
.sidebar-link.active { background: #e6f0ff; color: #1d4ed8; }
|
||||
|
||||
.sidebar-icon { width: 20px; height: 20px; flex-shrink: 0; }
|
||||
|
||||
.sidebar-section {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
padding: 4px 18px;
|
||||
margin-bottom: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.sidebar-section { font-size: 10px; font-weight: 700; color: #94a3b8; letter-spacing: 1px; text-transform: uppercase; padding: 4px 18px; margin-bottom: 6px; margin-top: 6px; }
|
||||
.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-logout button { width: 100%; border: none; background: transparent; color: #ef4444; font-weight: 600; padding: 10px; text-align: left; }
|
||||
|
||||
.main { flex: 1; background: #f5f9ff; display: flex; flex-direction: column; }
|
||||
|
||||
.topbar {
|
||||
background: #2b8ef3;
|
||||
margin: 20px;
|
||||
padding: 16px 24px;
|
||||
border-radius: 16px;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topbar { background: #2b8ef3; margin: 20px; padding: 16px 24px; border-radius: 16px; color: white; display: flex; justify-content: space-between; align-items: center; }
|
||||
.topbar-left { font-weight: 600; font-size: 16px; }
|
||||
.topbar-right { display: flex; align-items: center; gap: 16px; }
|
||||
.topbar-right { display: flex; align-items: center; gap: 14px; }
|
||||
.topbar-icon { width: 24px; height: 24px; cursor: pointer; }
|
||||
|
||||
.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; }
|
||||
.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; text-decoration: none; }
|
||||
.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; }
|
||||
</style>
|
||||
|
||||
@stack('styles')
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="admin-wrapper">
|
||||
|
||||
|
|
@ -104,67 +54,39 @@
|
|||
</div>
|
||||
|
||||
<nav class="sidebar-menu">
|
||||
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/home.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Dashboard</span>
|
||||
<a href="{{ route('admin.dashboard') }}" class="sidebar-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/home.png') }}" class="sidebar-icon" alt=""><span>Dashboard</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar-section">Data Master</div>
|
||||
|
||||
<a href="{{ route('admin.guru.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.guru.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Daftar Guru</span>
|
||||
<a href="{{ route('admin.guru.index') }}" class="sidebar-link {{ request()->routeIs('admin.guru.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon" alt=""><span>Daftar Guru</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('admin.kelas.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.kelas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/kelas.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Daftar Kelas</span>
|
||||
<a href="{{ route('admin.kelas.index') }}" class="sidebar-link {{ request()->routeIs('admin.kelas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/kelas.png') }}" class="sidebar-icon" alt=""><span>Daftar Kelas</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('admin.siswa.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.siswa.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Daftar Siswa</span>
|
||||
<a href="{{ route('admin.siswa.index') }}" class="sidebar-link {{ request()->routeIs('admin.siswa.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon" alt=""><span>Daftar Siswa</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('admin.mapel.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.mapel.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Mata Pelajaran</span>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="sidebar-link {{ request()->routeIs('admin.mapel.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt=""><span>Mata Pelajaran</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar-section">Konten Guru</div>
|
||||
|
||||
<a href="{{ route('admin.materi.history') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.materi.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt="">
|
||||
<span>History Materi</span>
|
||||
<a href="{{ route('admin.materi.history') }}" class="sidebar-link {{ request()->routeIs('admin.materi.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt=""><span>History Materi</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('admin.tugas.history') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.tugas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon" alt="">
|
||||
<span>History Tugas</span>
|
||||
<a href="{{ route('admin.tugas.history') }}" class="sidebar-link {{ request()->routeIs('admin.tugas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon" alt=""><span>History Tugas</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar-section">Gamifikasi</div>
|
||||
|
||||
<a href="{{ route('admin.challenge.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.challenge.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/challenge.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Challenge</span>
|
||||
<a href="{{ route('admin.challenge.index') }}" class="sidebar-link {{ request()->routeIs('admin.challenge.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/challenge.png') }}" class="sidebar-icon" alt=""><span>Challenge</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('admin.leaderboard.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('admin.leaderboard.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/lb.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Leaderboard</span>
|
||||
<a href="{{ route('admin.leaderboard.index') }}" class="sidebar-link {{ request()->routeIs('admin.leaderboard.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/lb.png') }}" class="sidebar-icon" alt=""><span>Leaderboard</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
<form action="{{ route('admin.logout') }}" method="POST" class="sidebar-logout">
|
||||
|
|
@ -180,7 +102,19 @@ class="sidebar-link {{ request()->routeIs('admin.leaderboard.*') ? 'active' : ''
|
|||
</div>
|
||||
<div class="topbar-right">
|
||||
<img src="{{ asset('images/icon/sidebar/notif.png') }}" class="topbar-icon" alt="Notification">
|
||||
<img src="{{ asset('images/icon/sidebar/profil.png') }}" class="topbar-icon" alt="Profile">
|
||||
|
||||
@php $admin = Auth::guard('admin')->user(); @endphp
|
||||
@if($admin->foto_profil)
|
||||
<a href="{{ route('admin.profile.edit') }}">
|
||||
<img src="{{ Storage::url($admin->foto_profil) }}" class="topbar-avatar" alt="Profil">
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('admin.profile.edit') }}" class="topbar-avatar-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', 'Edit Profil')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.page-title { font-size: 24px; font-weight: 800; margin-bottom: 6px; }
|
||||
.page-subtitle { font-size: 14px; color: #64748b; margin-bottom: 28px; }
|
||||
|
||||
.profile-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 2px solid #e5e5e5;
|
||||
padding: 32px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.foto-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 28px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.foto-preview {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #e6f0ff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.foto-placeholder {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
background: #e6f0ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border: 3px solid #e6f0ff;
|
||||
}
|
||||
|
||||
.foto-placeholder svg { width: 40px; height: 40px; color: #2b8ef3; }
|
||||
.foto-info p { font-size: 13px; color: #64748b; margin: 0 0 10px; }
|
||||
|
||||
.btn-upload {
|
||||
display: inline-block;
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 8px 18px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-upload:hover { background: #dbeeff; }
|
||||
|
||||
.field-group { margin-bottom: 18px; }
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.field-label span {
|
||||
color: #94a3b8;
|
||||
font-weight: 500;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
font-size: 11px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
background: #f8fafc;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 11px 14px;
|
||||
font-size: 14px;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #1e293b;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.field-input:focus {
|
||||
border-color: #2b8ef3;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(43,142,243,0.1);
|
||||
}
|
||||
|
||||
.divider { border: none; border-top: 1px solid #f1f5f9; margin: 24px 0; }
|
||||
|
||||
.btn-save {
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 12px 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 14px rgba(43,142,243,0.3);
|
||||
}
|
||||
|
||||
.btn-save:hover { background: #1a7ae0; transform: translateY(-1px); }
|
||||
|
||||
.alert-success {
|
||||
background: #f0fdf4;
|
||||
border: 1.5px solid #86efac;
|
||||
color: #166534;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fef2f2;
|
||||
border: 1.5px solid #fca5a5;
|
||||
color: #991b1b;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
@php $admin = Auth::guard('admin')->user(); @endphp
|
||||
|
||||
<h3 class="page-title">Edit Profil</h3>
|
||||
<p class="page-subtitle">Perbarui username, foto profil, dan password akunmu.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert-success">✓ {{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="alert-error">
|
||||
<ul style="margin:0;padding-left:16px">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.profile.update') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="profile-card">
|
||||
|
||||
<div class="foto-wrap">
|
||||
@if($admin->foto_profil)
|
||||
<img src="{{ Storage::url($admin->foto_profil) }}" class="foto-preview" id="foto-preview" alt="Foto Profil">
|
||||
@else
|
||||
<div class="foto-placeholder" id="foto-placeholder">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<img src="" class="foto-preview" id="foto-preview" alt="" style="display:none">
|
||||
@endif
|
||||
|
||||
<div class="foto-info">
|
||||
<p>Format: JPG, PNG, WEBP. Maks. 2MB.</p>
|
||||
<label for="foto_profil" class="btn-upload">Pilih Foto</label>
|
||||
<input type="file" name="foto_profil" id="foto_profil" accept="image/*" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Username bisa diubah untuk admin --}}
|
||||
<div class="field-group">
|
||||
<label class="field-label">Username</label>
|
||||
<input type="text" name="username" class="field-input"
|
||||
value="{{ old('username', $admin->username) }}" required>
|
||||
</div>
|
||||
|
||||
<hr class="divider">
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Password Baru <span>(kosongkan jika tidak ingin mengubah)</span></label>
|
||||
<input type="password" name="password" class="field-input"
|
||||
placeholder="Masukkan password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Konfirmasi Password Baru</label>
|
||||
<input type="password" name="password_confirmation" class="field-input"
|
||||
placeholder="Ulangi password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div style="margin-top:8px">
|
||||
<button type="submit" class="btn-save">Simpan Perubahan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('foto_profil').addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (!file) return;
|
||||
const url = URL.createObjectURL(file);
|
||||
const preview = document.getElementById('foto-preview');
|
||||
const placeholder = document.getElementById('foto-placeholder');
|
||||
preview.src = url;
|
||||
preview.style.display = 'block';
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -9,77 +9,39 @@
|
|||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body { font-family: 'Poppins', sans-serif; background-color: #f8f9fa; margin: 0; }
|
||||
.wrapper { display: flex; min-height: 100vh; }
|
||||
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
background: #ffffff;
|
||||
border-right: 2px solid #e6f0ff;
|
||||
padding: 30px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar { width: 260px; background: #ffffff; border-right: 2px solid #e6f0ff; padding: 30px 20px; display: flex; flex-direction: column; }
|
||||
.sidebar-logo { text-align: center; margin-bottom: 40px; }
|
||||
.sidebar-logo img { width: 90px; }
|
||||
|
||||
.sidebar-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 18px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 12px;
|
||||
color: #64748b;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-link { display: flex; align-items: center; gap: 12px; padding: 12px 18px; margin-bottom: 12px; border-radius: 12px; color: #64748b; text-decoration: none; font-weight: 500; transition: all 0.2s ease; }
|
||||
.sidebar-link:hover { 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; }
|
||||
|
||||
.main { flex: 1; background: #f5f9ff; display: flex; flex-direction: column; }
|
||||
|
||||
.topbar {
|
||||
background: #2b8ef3;
|
||||
margin: 20px;
|
||||
padding: 16px 24px;
|
||||
border-radius: 16px;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.topbar { background: #2b8ef3; margin: 20px; padding: 16px 24px; border-radius: 16px; color: white; display: flex; justify-content: space-between; align-items: center; }
|
||||
.topbar-left { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 16px; }
|
||||
.topbar-waving { width: 26px; height: 26px; }
|
||||
|
||||
.topbar-right { display: flex; align-items: center; gap: 16px; }
|
||||
.topbar-right { display: flex; align-items: center; gap: 14px; }
|
||||
.topbar-icon { width: 24px; height: 24px; cursor: pointer; }
|
||||
|
||||
.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; }
|
||||
.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; text-decoration: none; }
|
||||
.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; }
|
||||
</style>
|
||||
|
||||
@stack('styles')
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
|
||||
|
|
@ -88,40 +50,23 @@
|
|||
<img src="{{ asset('images/logo/logosmk.png') }}" alt="Logo">
|
||||
</div>
|
||||
|
||||
<a href="{{ route('guru.dashboard') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.dashboard') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/home.png') }}" class="sidebar-icon">
|
||||
<span>Dashboard</span>
|
||||
<a href="{{ route('guru.dashboard') }}" class="sidebar-link {{ request()->routeIs('guru.dashboard') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/home.png') }}" class="sidebar-icon"><span>Dashboard</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('guru.guru.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.guru.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon">
|
||||
<span>Daftar Guru</span>
|
||||
<a href="{{ route('guru.guru.index') }}" class="sidebar-link {{ request()->routeIs('guru.guru.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/guru.png') }}" class="sidebar-icon"><span>Daftar Guru</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('guru.kelas.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.kelas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/kelas.png') }}" class="sidebar-icon">
|
||||
<span>Daftar Kelas</span>
|
||||
<a href="{{ route('guru.kelas.index') }}" class="sidebar-link {{ request()->routeIs('guru.kelas.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/kelas.png') }}" class="sidebar-icon"><span>Daftar Kelas</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('guru.siswa.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.siswa.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon">
|
||||
<span>Daftar Siswa</span>
|
||||
<a href="{{ route('guru.siswa.index') }}" class="sidebar-link {{ request()->routeIs('guru.siswa.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon"><span>Daftar Siswa</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('guru.mapel.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.mapel.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon">
|
||||
<span>Mata Pelajaran</span>
|
||||
<a href="{{ route('guru.mapel.index') }}" class="sidebar-link {{ request()->routeIs('guru.mapel.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon"><span>Mata Pelajaran</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('guru.leaderboard.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('guru.leaderboard.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/lb.png') }}" class="sidebar-icon">
|
||||
<span>Leaderboard</span>
|
||||
<a href="{{ route('guru.leaderboard.index') }}" class="sidebar-link {{ request()->routeIs('guru.leaderboard.*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/lb.png') }}" class="sidebar-icon"><span>Leaderboard</span>
|
||||
</a>
|
||||
|
||||
<form action="{{ route('guru.logout') }}" method="POST" class="sidebar-logout">
|
||||
|
|
@ -138,7 +83,19 @@ class="sidebar-link {{ request()->routeIs('guru.leaderboard.*') ? 'active' : ''
|
|||
</div>
|
||||
<div class="topbar-right">
|
||||
<img src="{{ asset('images/icon/sidebar/notif.png') }}" class="topbar-icon" alt="Notification">
|
||||
<img src="{{ asset('images/icon/sidebar/profil.png') }}" class="topbar-icon" alt="Profile">
|
||||
|
||||
@php $guru = Auth::guard('guru')->user(); @endphp
|
||||
@if($guru->foto_profil)
|
||||
<a href="{{ route('guru.profile.edit') }}">
|
||||
<img src="{{ Storage::url($guru->foto_profil) }}" class="topbar-avatar" alt="Profil">
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('guru.profile.edit') }}" class="topbar-avatar-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -148,7 +105,6 @@ class="sidebar-link {{ request()->routeIs('guru.leaderboard.*') ? 'active' : ''
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
@extends('guru.layouts.app')
|
||||
|
||||
@section('title', 'Edit Profil')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.page-title { font-size: 24px; font-weight: 800; margin-bottom: 6px; }
|
||||
.page-subtitle { font-size: 14px; color: #64748b; margin-bottom: 28px; }
|
||||
|
||||
.profile-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 2px solid #e5e5e5;
|
||||
padding: 32px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.foto-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 28px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.foto-preview {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #e6f0ff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.foto-placeholder {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
background: #e6f0ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border: 3px solid #e6f0ff;
|
||||
}
|
||||
|
||||
.foto-placeholder svg { width: 40px; height: 40px; color: #2b8ef3; }
|
||||
.foto-info p { font-size: 13px; color: #64748b; margin: 0 0 10px; }
|
||||
|
||||
.btn-upload {
|
||||
display: inline-block;
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 8px 18px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-upload:hover { background: #dbeeff; }
|
||||
|
||||
.field-group { margin-bottom: 18px; }
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.field-label span {
|
||||
color: #94a3b8;
|
||||
font-weight: 500;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
font-size: 11px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
background: #f8fafc;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 11px 14px;
|
||||
font-size: 14px;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #1e293b;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.field-input:focus {
|
||||
border-color: #2b8ef3;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(43,142,243,0.1);
|
||||
}
|
||||
|
||||
.field-input:disabled {
|
||||
background: #f1f5f9;
|
||||
color: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.divider { border: none; border-top: 1px solid #f1f5f9; margin: 24px 0; }
|
||||
|
||||
.btn-save {
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 12px 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 14px rgba(43,142,243,0.3);
|
||||
}
|
||||
|
||||
.btn-save:hover { background: #1a7ae0; transform: translateY(-1px); }
|
||||
|
||||
.alert-success {
|
||||
background: #f0fdf4;
|
||||
border: 1.5px solid #86efac;
|
||||
color: #166534;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fef2f2;
|
||||
border: 1.5px solid #fca5a5;
|
||||
color: #991b1b;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
@php $guru = Auth::guard('guru')->user(); @endphp
|
||||
|
||||
<h3 class="page-title">Edit Profil</h3>
|
||||
<p class="page-subtitle">Perbarui foto profil dan password akunmu.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert-success">✓ {{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="alert-error">
|
||||
<ul style="margin:0;padding-left:16px">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('guru.profile.update') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="profile-card">
|
||||
|
||||
<div class="foto-wrap">
|
||||
@if($guru->foto_profil)
|
||||
<img src="{{ Storage::url($guru->foto_profil) }}" class="foto-preview" id="foto-preview" alt="Foto Profil">
|
||||
@else
|
||||
<div class="foto-placeholder" id="foto-placeholder">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<img src="" class="foto-preview" id="foto-preview" alt="" style="display:none">
|
||||
@endif
|
||||
|
||||
<div class="foto-info">
|
||||
<p>Format: JPG, PNG, WEBP. Maks. 2MB.</p>
|
||||
<label for="foto_profil" class="btn-upload">Pilih Foto</label>
|
||||
<input type="file" name="foto_profil" id="foto_profil" accept="image/*" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">NIP <span>(tidak dapat diubah)</span></label>
|
||||
<input type="text" class="field-input" value="{{ $guru->nip }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Nama Lengkap <span>(tidak dapat diubah)</span></label>
|
||||
<input type="text" class="field-input" value="{{ $guru->nama }}" disabled>
|
||||
</div>
|
||||
|
||||
<hr class="divider">
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Password Baru <span>(kosongkan jika tidak ingin mengubah)</span></label>
|
||||
<input type="password" name="password" class="field-input"
|
||||
placeholder="Masukkan password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Konfirmasi Password Baru</label>
|
||||
<input type="password" name="password_confirmation" class="field-input"
|
||||
placeholder="Ulangi password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div style="margin-top:8px">
|
||||
<button type="submit" class="btn-save">Simpan Perubahan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('foto_profil').addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (!file) return;
|
||||
const url = URL.createObjectURL(file);
|
||||
const preview = document.getElementById('foto-preview');
|
||||
const placeholder = document.getElementById('foto-placeholder');
|
||||
preview.src = url;
|
||||
preview.style.display = 'block';
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -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; }
|
||||
</style>
|
||||
|
||||
@stack('styles')
|
||||
|
|
@ -202,38 +158,32 @@
|
|||
<body>
|
||||
<div class="siswa-wrapper">
|
||||
|
||||
<!-- SIDEBAR (collapsed by default) -->
|
||||
<aside class="sidebar collapsed" id="mainSidebar">
|
||||
<div class="sidebar-logo">
|
||||
<img src="{{ asset('images/logo/logosmk.png') }}" alt="Logo SMK">
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-menu">
|
||||
|
||||
<a href="{{ route('siswa.dashboard') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.dashboard') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/home.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('siswa.materi.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.materi*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Materi</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('siswa.tugas.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.tugas*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Tugas</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('siswa.challenge.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.challenge*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/challenge.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Challenge</span>
|
||||
</a>
|
||||
|
||||
<a href="{{ route('siswa.leaderboard.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.leaderboard*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/lb.png') }}" class="sidebar-icon" alt="">
|
||||
|
|
@ -243,24 +193,18 @@ class="sidebar-link {{ request()->routeIs('siswa.leaderboard*') ? 'active' : ''
|
|||
|
||||
<form action="{{ route('siswa.logout') }}" method="POST" class="sidebar-logout">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-danger w-100">
|
||||
Logout
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger w-100">Logout</button>
|
||||
</form>
|
||||
</aside>
|
||||
|
||||
<!-- TOGGLE ARROW BUTTON (collapsed by default karena sidebar tertutup) -->
|
||||
<button class="sidebar-toggle-btn collapsed" id="sidebarToggleBtn" title="Toggle Sidebar">
|
||||
<!-- Panah kiri (← artinya tutup sidebar) -->
|
||||
<svg class="toggle-arrow" viewBox="0 0 24 24">
|
||||
<polyline points="15 18 9 12 15 6"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- MAIN -->
|
||||
<div class="main" id="mainContent">
|
||||
|
||||
<!-- TOPBAR -->
|
||||
<header class="topbar">
|
||||
<div class="topbar-left">
|
||||
👋 Hai, {{ Auth::guard('siswa')->user()->nama ?? 'Siswa' }}
|
||||
|
|
@ -268,62 +212,47 @@ class="sidebar-link {{ request()->routeIs('siswa.leaderboard*') ? 'active' : ''
|
|||
|
||||
<div class="topbar-right">
|
||||
<img src="{{ asset('images/icon/sidebar/notif.png') }}" class="topbar-icon" alt="Notification">
|
||||
<img src="{{ asset('images/icon/sidebar/profil.png') }}" class="topbar-icon" alt="Profile">
|
||||
|
||||
{{-- Foto profil atau icon default --}}
|
||||
@php $siswa = Auth::guard('siswa')->user(); @endphp
|
||||
@if($siswa->foto_profil)
|
||||
<a href="{{ route('siswa.profile.edit') }}">
|
||||
<img src="{{ Storage::url($siswa->foto_profil) }}"
|
||||
class="topbar-avatar" alt="Profil">
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('siswa.profile.edit') }}" class="topbar-avatar-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- CONTENT -->
|
||||
<main class="content">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
const sidebar = document.getElementById('mainSidebar');
|
||||
const toggleBtn = document.getElementById('sidebarToggleBtn');
|
||||
const SIDEBAR_W = 260; // harus sama dengan CSS .sidebar width
|
||||
const sidebar = document.getElementById('mainSidebar');
|
||||
const toggleBtn = document.getElementById('sidebarToggleBtn');
|
||||
const SIDEBAR_W = 260;
|
||||
|
||||
function updateTogglePosition(isCollapsed) {
|
||||
if (isCollapsed) {
|
||||
toggleBtn.style.left = '0px';
|
||||
toggleBtn.classList.add('collapsed');
|
||||
} else {
|
||||
toggleBtn.style.left = SIDEBAR_W + 'px';
|
||||
toggleBtn.classList.remove('collapsed');
|
||||
}
|
||||
toggleBtn.style.left = isCollapsed ? '0px' : SIDEBAR_W + 'px';
|
||||
isCollapsed ? toggleBtn.classList.add('collapsed') : toggleBtn.classList.remove('collapsed');
|
||||
}
|
||||
|
||||
toggleBtn.addEventListener('click', function () {
|
||||
const isCurrentlyCollapsed = sidebar.classList.contains('collapsed');
|
||||
|
||||
if (isCurrentlyCollapsed) {
|
||||
// Buka sidebar
|
||||
sidebar.classList.remove('collapsed');
|
||||
updateTogglePosition(false);
|
||||
localStorage.setItem('sidebarOpen', 'true');
|
||||
} else {
|
||||
// Tutup sidebar
|
||||
sidebar.classList.add('collapsed');
|
||||
updateTogglePosition(true);
|
||||
localStorage.setItem('sidebarOpen', 'false');
|
||||
}
|
||||
const isCollapsed = sidebar.classList.contains('collapsed');
|
||||
sidebar.classList.toggle('collapsed');
|
||||
updateTogglePosition(!isCollapsed);
|
||||
localStorage.setItem('sidebarOpen', isCollapsed ? 'true' : 'false');
|
||||
});
|
||||
|
||||
// Tidak perlu restore dari localStorage karena kita mau selalu collapsed saat login baru.
|
||||
// Tapi kalau mau ingat preferensi user dalam satu sesi browsing, uncomment ini:
|
||||
/*
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const saved = localStorage.getItem('sidebarOpen');
|
||||
if (saved === 'true') {
|
||||
sidebar.classList.remove('collapsed');
|
||||
updateTogglePosition(false);
|
||||
}
|
||||
});
|
||||
*/
|
||||
</script>
|
||||
|
||||
@stack('scripts')
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
|
|
@ -148,9 +73,7 @@
|
|||
<h3 class="page-title">🏅 Leaderboard</h3>
|
||||
<p class="page-subtitle">Peringkat siswa berdasarkan total EXP yang dikumpulkan.</p>
|
||||
|
||||
<span class="semester-badge">
|
||||
Semester {{ $semester }} · {{ $tahunAjaran }}
|
||||
</span>
|
||||
<span class="semester-badge">Semester {{ $semester }} · {{ $tahunAjaran }}</span>
|
||||
|
||||
@if($leaderboard->isEmpty())
|
||||
<div class="empty-state">
|
||||
|
|
@ -160,9 +83,8 @@
|
|||
</div>
|
||||
@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)
|
||||
<div class="podium-wrap">
|
||||
|
||||
{{-- Rank 2 --}}
|
||||
@if($second)
|
||||
<div class="podium-item rank-2">
|
||||
<div class="podium-avatar">{{ strtoupper(substr($second['nama'], 0, 1)) }}</div>
|
||||
<div class="podium-avatar">
|
||||
@if(!empty($second['foto_profil']))
|
||||
<img src="{{ Storage::url($second['foto_profil']) }}" alt="">
|
||||
@else
|
||||
{{ strtoupper(substr($second['nama'], 0, 1)) }}
|
||||
@endif
|
||||
</div>
|
||||
<div class="podium-name">{{ $second['nama'] }}</div>
|
||||
<div class="podium-exp">⭐ {{ number_format($second['exp']) }}</div>
|
||||
<div class="podium-bar">2</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Rank 1 --}}
|
||||
<div class="podium-item rank-1">
|
||||
<div class="podium-avatar">
|
||||
<span class="podium-crown">👑</span>
|
||||
{{ strtoupper(substr($first['nama'], 0, 1)) }}
|
||||
@if(!empty($first['foto_profil']))
|
||||
<img src="{{ Storage::url($first['foto_profil']) }}" alt="">
|
||||
@else
|
||||
{{ strtoupper(substr($first['nama'], 0, 1)) }}
|
||||
@endif
|
||||
</div>
|
||||
<div class="podium-name">{{ $first['nama'] }}</div>
|
||||
<div class="podium-exp">⭐ {{ number_format($first['exp']) }}</div>
|
||||
<div class="podium-bar">1</div>
|
||||
</div>
|
||||
|
||||
{{-- Rank 3 --}}
|
||||
@if($third)
|
||||
<div class="podium-item rank-3">
|
||||
<div class="podium-avatar">{{ strtoupper(substr($third['nama'], 0, 1)) }}</div>
|
||||
<div class="podium-avatar">
|
||||
@if(!empty($third['foto_profil']))
|
||||
<img src="{{ Storage::url($third['foto_profil']) }}" alt="">
|
||||
@else
|
||||
{{ strtoupper(substr($third['nama'], 0, 1)) }}
|
||||
@endif
|
||||
</div>
|
||||
<div class="podium-name">{{ $third['nama'] }}</div>
|
||||
<div class="podium-exp">⭐ {{ number_format($third['exp']) }}</div>
|
||||
<div class="podium-bar">3</div>
|
||||
|
|
@ -205,9 +140,13 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
{{-- My Rank Banner --}}
|
||||
@if($myRank)
|
||||
<div class="my-rank-banner">
|
||||
@if(!empty($myRank['foto_profil']))
|
||||
<img src="{{ Storage::url($myRank['foto_profil']) }}" class="my-rank-avatar" alt="">
|
||||
@else
|
||||
<div class="my-rank-avatar-placeholder">{{ strtoupper(substr($myRank['nama'], 0, 1)) }}</div>
|
||||
@endif
|
||||
<div class="my-rank-num">#{{ $myRank['ranking'] }}</div>
|
||||
<div class="my-rank-info">
|
||||
<div class="my-rank-label">Posisimu saat ini</div>
|
||||
|
|
@ -218,16 +157,13 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Tabel semua --}}
|
||||
<div class="custom-card">
|
||||
<p class="section-title">📋 Semua Peringkat</p>
|
||||
|
||||
@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
|
||||
<div class="lb-row {{ $isMe ? 'highlight' : '' }}">
|
||||
<div class="lb-rank {{ $rankClass }}">
|
||||
|
|
@ -237,10 +173,19 @@
|
|||
@else {{ $item['ranking'] }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if(!empty($item['foto_profil']))
|
||||
<img src="{{ Storage::url($item['foto_profil']) }}" class="lb-avatar" alt="">
|
||||
@else
|
||||
<div class="lb-avatar-placeholder">{{ strtoupper(substr($item['nama'], 0, 1)) }}</div>
|
||||
@endif
|
||||
|
||||
<div style="flex:1">
|
||||
<div class="lb-nama">
|
||||
{{ $item['nama'] }}
|
||||
@if($isMe) <span style="background:#c4b5fd;color:#4c1d95;font-size:10px;padding:2px 7px;border-radius:99px;font-weight:700;margin-left:4px">Kamu</span> @endif
|
||||
@if($isMe)
|
||||
<span style="background:#c4b5fd;color:#4c1d95;font-size:10px;padding:2px 7px;border-radius:99px;font-weight:700;margin-left:4px">Kamu</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="lb-nisn">{{ $item['nisn'] }}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,255 @@
|
|||
@extends('siswa.layouts.app')
|
||||
|
||||
@section('title', 'Edit Profil')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.page-title { font-size: 24px; font-weight: 800; margin-bottom: 6px; }
|
||||
.page-subtitle { font-size: 14px; color: #64748b; margin-bottom: 28px; }
|
||||
|
||||
.profile-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
border: 2px solid #e5e5e5;
|
||||
padding: 32px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
/* Foto profil */
|
||||
.foto-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 28px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.foto-preview {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #e6f0ff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.foto-placeholder {
|
||||
width: 88px; height: 88px;
|
||||
border-radius: 50%;
|
||||
background: #e6f0ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border: 3px solid #e6f0ff;
|
||||
}
|
||||
|
||||
.foto-placeholder svg { width: 40px; height: 40px; color: #2b8ef3; }
|
||||
|
||||
.foto-info p { font-size: 13px; color: #64748b; margin: 0 0 10px; }
|
||||
|
||||
.btn-upload {
|
||||
display: inline-block;
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 8px 18px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-upload:hover { background: #dbeeff; }
|
||||
|
||||
/* Form fields */
|
||||
.field-group { margin-bottom: 18px; }
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.field-label span {
|
||||
color: #94a3b8;
|
||||
font-weight: 500;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
font-size: 11px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 100%;
|
||||
background: #f8fafc;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 11px 14px;
|
||||
font-size: 14px;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #1e293b;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.field-input:focus {
|
||||
border-color: #2b8ef3;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(43,142,243,0.1);
|
||||
}
|
||||
|
||||
.field-input:disabled {
|
||||
background: #f1f5f9;
|
||||
color: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.field-hint { font-size: 11px; color: #94a3b8; margin-top: 4px; }
|
||||
|
||||
.divider {
|
||||
border: none;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 12px 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 14px rgba(43,142,243,0.3);
|
||||
}
|
||||
|
||||
.btn-save:hover { background: #1a7ae0; transform: translateY(-1px); }
|
||||
|
||||
.alert-success {
|
||||
background: #f0fdf4;
|
||||
border: 1.5px solid #86efac;
|
||||
color: #166534;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fef2f2;
|
||||
border: 1.5px solid #fca5a5;
|
||||
color: #991b1b;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
@php $siswa = Auth::guard('siswa')->user(); @endphp
|
||||
|
||||
<h3 class="page-title">Edit Profil</h3>
|
||||
<p class="page-subtitle">Perbarui foto profil dan password akunmu.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert-success">✓ {{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="alert-error">
|
||||
<ul style="margin:0;padding-left:16px">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('siswa.profile.update') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="profile-card">
|
||||
|
||||
{{-- Foto Profil --}}
|
||||
<div class="foto-wrap">
|
||||
@if($siswa->foto_profil)
|
||||
<img src="{{ Storage::url($siswa->foto_profil) }}" class="foto-preview" id="foto-preview" alt="Foto Profil">
|
||||
@else
|
||||
<div class="foto-placeholder" id="foto-placeholder">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<img src="" class="foto-preview" id="foto-preview" alt="" style="display:none">
|
||||
@endif
|
||||
|
||||
<div class="foto-info">
|
||||
<p>Format: JPG, PNG, WEBP. Maks. 2MB.</p>
|
||||
<label for="foto_profil" class="btn-upload">Pilih Foto</label>
|
||||
<input type="file" name="foto_profil" id="foto_profil" accept="image/*" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Info tidak bisa diubah --}}
|
||||
<div class="field-group">
|
||||
<label class="field-label">NISN <span>(tidak dapat diubah)</span></label>
|
||||
<input type="text" class="field-input" value="{{ $siswa->nisn }}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Nama Lengkap <span>(tidak dapat diubah)</span></label>
|
||||
<input type="text" class="field-input" value="{{ $siswa->nama }}" disabled>
|
||||
</div>
|
||||
|
||||
<hr class="divider">
|
||||
|
||||
{{-- Password --}}
|
||||
<div class="field-group">
|
||||
<label class="field-label">Password Baru <span>(kosongkan jika tidak ingin mengubah)</span></label>
|
||||
<input type="password" name="password" class="field-input"
|
||||
placeholder="Masukkan password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label">Konfirmasi Password Baru</label>
|
||||
<input type="password" name="password_confirmation" class="field-input"
|
||||
placeholder="Ulangi password baru" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div style="margin-top:8px">
|
||||
<button type="submit" class="btn-save">Simpan Perubahan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('foto_profil').addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (!file) return;
|
||||
const url = URL.createObjectURL(file);
|
||||
const preview = document.getElementById('foto-preview');
|
||||
const placeholder = document.getElementById('foto-placeholder');
|
||||
preview.src = url;
|
||||
preview.style.display = 'block';
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Reference in New Issue