Bismillah Lancar sidang
Bagian yang kurang 1. Bagian Tugas guru filter nilai rata rata 2. Nilai Belum Tervalidasi 3. Ganti Passwoard di profil
This commit is contained in:
parent
c0d7a893cf
commit
3bdc385a6d
|
|
@ -13,77 +13,164 @@
|
|||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class AuthController extends Controller
|
||||
|
||||
{
|
||||
// ================= LOGIN =================
|
||||
// ================= SHOW LOGIN =================
|
||||
public function showLogin()
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function login(Request $request)
|
||||
// ================= LOGIN BIASA (email + password) =================
|
||||
public function login(Request $request)
|
||||
{
|
||||
$credentials = $request->only('email', 'password');
|
||||
|
||||
$user = \App\Models\User::with('guru')
|
||||
->where('email', $request->email)
|
||||
->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'Email tidak ditemukan');
|
||||
}
|
||||
|
||||
if (!$user->guru || $user->guru->status !== 'aktif') {
|
||||
return back()->with('error', 'Akun anda telah dinonaktifkan');
|
||||
}
|
||||
|
||||
if (Auth::attempt($credentials)) {
|
||||
|
||||
$request->session()->regenerate();
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
// 🔥 HANYA JIKA PASSWORD DEFAULT
|
||||
// Notifikasi login pertama → HANYA kirim email, tidak WA
|
||||
// Hilangkan logika WA di sini agar tidak mengganggu flow OTP
|
||||
if ($user->is_default_password) {
|
||||
|
||||
// ================= EMAIL =================
|
||||
Mail::to($user->email)->send(new LoginNotification($user));
|
||||
|
||||
// ================= WHATSAPP =================
|
||||
$telepon = $user->getTeleponLengkap();
|
||||
|
||||
if ($telepon) {
|
||||
|
||||
// normalisasi nomor
|
||||
$no = preg_replace('/[^0-9]/', '', $telepon);
|
||||
if (substr($no, 0, 1) == '0') {
|
||||
$no = '62' . substr($no, 1);
|
||||
}
|
||||
|
||||
$pesan = "🔐 LOGIN PERTAMA\n";
|
||||
$pesan .= "Nama: {$user->name}\n";
|
||||
$pesan .= "Email: {$user->email}\n";
|
||||
$pesan .= "Gunakan password default\n";
|
||||
$pesan .= "Segera ganti password!\n";
|
||||
$pesan .= "Waktu: " . now();
|
||||
|
||||
Http::asForm()->withHeaders([
|
||||
'Authorization' => env('FONNTE_TOKEN')
|
||||
])->post('https://api.fonnte.com/send', [
|
||||
'target' => $no,
|
||||
'message' => $pesan,
|
||||
]);
|
||||
}
|
||||
Mail::to($user->email)->send(new \App\Mail\LoginNotification($user));
|
||||
}
|
||||
|
||||
// 🔥 REDIRECT ROLE
|
||||
if ($user->role == 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
if ($user->role == 'guru') {
|
||||
return redirect()->route('guru.dashboard');
|
||||
}
|
||||
|
||||
if ($user->role == 'siswa') {
|
||||
return redirect()->route('siswa.dashboard');
|
||||
}
|
||||
|
||||
return redirect('/login');
|
||||
return $this->redirectByRole($user);
|
||||
}
|
||||
|
||||
return back()->with('error', 'Email atau password salah');
|
||||
}
|
||||
|
||||
// ================= KIRIM OTP =================
|
||||
// Dipanggil dari form Langkah 1 di halaman login
|
||||
public function sendOtp(Request $request)
|
||||
{
|
||||
$method = $request->input('method', 'email'); // 'email' atau 'wa'
|
||||
|
||||
if ($method === 'email') {
|
||||
$request->validate([
|
||||
'email' => 'required|email|exists:users,email',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
} else {
|
||||
$request->validate([
|
||||
'telepon' => 'required|string',
|
||||
]);
|
||||
|
||||
$no = preg_replace('/[^0-9]/', '', $request->telepon);
|
||||
if (str_starts_with($no, '0')) {
|
||||
$no = '62' . substr($no, 1);
|
||||
}
|
||||
|
||||
$user = User::whereHas('guru', function ($q) use ($no) {
|
||||
$q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$no}%"]);
|
||||
})->orWhereHas('siswa', function ($q) use ($no) {
|
||||
$q->whereRaw("REGEXP_REPLACE(telepon, '[^0-9]', '') LIKE ?", ["%{$no}%"]);
|
||||
})->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'Nomor WhatsApp tidak ditemukan.');
|
||||
}
|
||||
}
|
||||
|
||||
// Kirim OTP sesuai method — email SAJA atau WA SAJA
|
||||
app(\App\Services\OtpService::class)->send($user, $method);
|
||||
|
||||
// Simpan email ke session untuk prefill form login
|
||||
session(['email' => $user->email]);
|
||||
|
||||
return back()->with('otp_sent', 'OTP berhasil dikirim!');
|
||||
}
|
||||
|
||||
|
||||
// ================= LOGIN DENGAN OTP (Langkah 2) =================
|
||||
// Dipanggil dari form Langkah 2 di halaman login
|
||||
public function loginOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email',
|
||||
'password' => 'required',
|
||||
'otp' => 'nullable|digits:6',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'Email tidak ditemukan.');
|
||||
}
|
||||
|
||||
// Cek password
|
||||
if (!Hash::check($request->password, $user->password)) {
|
||||
return back()->with('error', 'Password salah.');
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// ADMIN → LANGSUNG LOGIN TANPA OTP
|
||||
// =================================================
|
||||
if ($user->role === 'admin') {
|
||||
|
||||
Auth::login($user);
|
||||
$request->session()->regenerate();
|
||||
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// GURU / SISWA WAJIB OTP
|
||||
// =================================================
|
||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
||||
|
||||
if (empty($request->otp)) {
|
||||
return back()->with('error', 'OTP wajib diisi.');
|
||||
}
|
||||
|
||||
if (!$user->otp) {
|
||||
return back()->with('error', 'OTP belum diminta.');
|
||||
}
|
||||
|
||||
if ((string)$request->otp !== (string)$user->otp) {
|
||||
return back()->with('error', 'Kode OTP salah.');
|
||||
}
|
||||
|
||||
if (!$user->otp_expired_at || now()->gt($user->otp_expired_at)) {
|
||||
return back()->with('error', 'OTP sudah kadaluarsa.');
|
||||
}
|
||||
|
||||
// Login
|
||||
Auth::login($user);
|
||||
$request->session()->regenerate();
|
||||
|
||||
// Hapus OTP
|
||||
$user->update([
|
||||
'otp' => null,
|
||||
'otp_expired_at' => null,
|
||||
]);
|
||||
|
||||
return match ($user->role) {
|
||||
'guru' => redirect()->route('guru.dashboard'),
|
||||
'siswa' => redirect()->route('siswa.dashboard'),
|
||||
default => redirect('/'),
|
||||
};
|
||||
}
|
||||
|
||||
return back()->with('error', 'Role tidak dikenali.');
|
||||
}
|
||||
|
||||
|
||||
// ================= UPDATE PASSWORD =================
|
||||
public function updatePassword(Request $request)
|
||||
{
|
||||
|
|
@ -93,12 +180,10 @@ public function updatePassword(Request $request)
|
|||
|
||||
$user = Auth::user();
|
||||
|
||||
// 🔥 UPDATE + FORCE REFRESH USER
|
||||
$user->password = Hash::make($request->password);
|
||||
$user->is_default_password = false;
|
||||
$user->save();
|
||||
|
||||
// 🔥 PENTING: refresh data user (hindari cache lama)
|
||||
Auth::setUser($user->fresh());
|
||||
|
||||
return $this->redirectByRole($user);
|
||||
|
|
@ -113,18 +198,18 @@ public function showRegister()
|
|||
public function register(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users',
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users',
|
||||
'password' => 'required|min:6',
|
||||
'role' => 'required'
|
||||
'role' => 'required'
|
||||
]);
|
||||
|
||||
User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
'is_default_password' => false // 🔥 register normal
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
'is_default_password' => false,
|
||||
]);
|
||||
|
||||
return redirect('/login')->with('success', 'Register berhasil');
|
||||
|
|
@ -141,134 +226,73 @@ public function logout(Request $request)
|
|||
return redirect('/login');
|
||||
}
|
||||
|
||||
// ================= BONUS: REDIRECT BY ROLE =================
|
||||
private function redirectByRole($user)
|
||||
// ================= VERIFY OTP (halaman terpisah jika ada) =================
|
||||
public function showVerifyOtp()
|
||||
{
|
||||
return view('auth.verify-otp');
|
||||
}
|
||||
|
||||
public function verifyOtp(Request $request)
|
||||
{
|
||||
if ((string) $request->otp === (string) session('otp')) {
|
||||
return redirect()->route('reset.password.form');
|
||||
}
|
||||
|
||||
return back()->with('error', 'OTP salah');
|
||||
}
|
||||
|
||||
// ================= RESET PASSWORD (via OTP lupa password) =================
|
||||
public function showResetPassword()
|
||||
{
|
||||
return view('auth.reset-password');
|
||||
}
|
||||
|
||||
public function resetPasswordOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|min:6|confirmed'
|
||||
]);
|
||||
|
||||
$user = User::find(session('otp_user'));
|
||||
|
||||
$user->update([
|
||||
'password' => Hash::make($request->password),
|
||||
'otp' => null,
|
||||
'otp_expired_at' => null,
|
||||
'is_default_password' => false,
|
||||
]);
|
||||
|
||||
session()->forget('otp_user');
|
||||
|
||||
return redirect('/login')->with('success', 'Password berhasil diubah');
|
||||
}
|
||||
|
||||
public function resetPassword(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|confirmed|min:6'
|
||||
]);
|
||||
|
||||
$user = \App\Models\User::where('email', session('email'))->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'User tidak ditemukan');
|
||||
}
|
||||
|
||||
$user->password = bcrypt($request->password);
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('login')->with('success', 'Password berhasil diubah');
|
||||
}
|
||||
|
||||
// ================= HELPER: REDIRECT BY ROLE =================
|
||||
private function redirectByRole($user)
|
||||
{
|
||||
return match ($user->role) {
|
||||
'admin' => redirect()->route('admin.dashboard'),
|
||||
'guru' => redirect()->route('guru.dashboard'),
|
||||
'guru' => redirect()->route('guru.dashboard'),
|
||||
'siswa' => redirect()->route('siswa.dashboard'),
|
||||
default => redirect('/login'),
|
||||
};
|
||||
}
|
||||
|
||||
public function verifyOtp(Request $request)
|
||||
{
|
||||
if ($request->otp == session('otp')) {
|
||||
|
||||
return redirect()->route('reset.password.form'); // ✅ INI WAJIB
|
||||
}
|
||||
|
||||
return back()->with('error', 'OTP salah');
|
||||
}
|
||||
|
||||
public function resetPasswordOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|min:6|confirmed'
|
||||
]);
|
||||
|
||||
$user = User::find(session('otp_user'));
|
||||
|
||||
$user->update([
|
||||
'password' => Hash::make($request->password),
|
||||
'otp' => null,
|
||||
'otp_expired_at' => null,
|
||||
'is_default_password' => false
|
||||
]);
|
||||
|
||||
session()->forget('otp_user');
|
||||
|
||||
return redirect('/login')->with('success', 'Password berhasil diubah');
|
||||
}
|
||||
|
||||
public function sendOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email|exists:users,email'
|
||||
]);
|
||||
|
||||
$otp = rand(100000, 999999);
|
||||
|
||||
session([
|
||||
'otp' => $otp,
|
||||
'email' => $request->email
|
||||
]);
|
||||
|
||||
// kirim email (Mailtrap sudah jalan)
|
||||
|
||||
\Mail::raw("Kode OTP kamu adalah: $otp", function ($message) use ($request) {
|
||||
$message->to($request->email)
|
||||
->subject('Kode OTP Reset Password');
|
||||
});
|
||||
|
||||
return redirect()->route('verify.otp.form');
|
||||
}
|
||||
|
||||
public function showVerifyOtp()
|
||||
{
|
||||
return view('auth.verify-otp');
|
||||
}
|
||||
|
||||
public function showResetPassword()
|
||||
{
|
||||
return view('auth.reset-password');
|
||||
}
|
||||
|
||||
public function resetPassword(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'password' => 'required|confirmed|min:6'
|
||||
]);
|
||||
|
||||
$user = \App\Models\User::where('email', session('email'))->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'User tidak ditemukan');
|
||||
}
|
||||
|
||||
$user->password = bcrypt($request->password);
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('login')->with('success', 'Password berhasil diubah');
|
||||
}
|
||||
|
||||
public function sendWhatsapp($nomor, $pesan)
|
||||
{
|
||||
$token = env('rJ5jmNfJ99gJFNdmjmTW');
|
||||
|
||||
// 🔥 NORMALISASI NOMOR (ANTI SALAH FORMAT)
|
||||
$nomor = preg_replace('/[^0-9]/', '', $nomor);
|
||||
|
||||
if (substr($nomor, 0, 1) == '0') {
|
||||
$nomor = '62' . substr($nomor, 1);
|
||||
}
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, array(
|
||||
CURLOPT_URL => "https://api.fonnte.com/send",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => [
|
||||
'target' => $nomor,
|
||||
'message' => $pesan,
|
||||
'countryCode' => '62'
|
||||
],
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: $token"
|
||||
],
|
||||
));
|
||||
|
||||
$response = curl_exec($curl);
|
||||
|
||||
if (curl_errno($curl)) {
|
||||
dd("CURL ERROR: " . curl_error($curl));
|
||||
}
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
// 🔥 DEBUG WAJIB
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,31 +13,66 @@ class DashboardController extends Controller
|
|||
{
|
||||
public function index()
|
||||
{
|
||||
// Statistik utama
|
||||
// =====================
|
||||
// 🔥 STATISTIK
|
||||
// =====================
|
||||
$jumlahSiswa = Siswa::count();
|
||||
$jumlahGuru = Guru::count();
|
||||
$jumlahKelas = Kelas::count();
|
||||
$jumlahMapel = MataPelajaran::count();
|
||||
|
||||
// Siswa terbaru
|
||||
// =====================
|
||||
// 🔥 SISWA TERBARU
|
||||
// =====================
|
||||
$siswaTerbaru = Siswa::with('kelas')
|
||||
->latest()
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// 🔥 Nilai tertinggi
|
||||
// =====================
|
||||
// 🔥 NILAI TERTINGGI
|
||||
$nilaiTertinggi = Nilai::with('siswa')
|
||||
->orderBy('nilai', 'desc')
|
||||
->selectRaw('*, (nilai_tugas + nilai_uts + nilai_uas)/3 as nilai_akhir')
|
||||
->orderBy('nilai_akhir', 'desc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// =====================
|
||||
// 🔥 CHART: SISWA PER KELAS
|
||||
// =====================
|
||||
$kelas = Kelas::withCount('siswa')->get();
|
||||
|
||||
$kelasLabels = $kelas->pluck('nama');
|
||||
$kelasData = $kelas->pluck('siswa_count');
|
||||
|
||||
// =====================
|
||||
// 🔥 CHART: DISTRIBUSI NILAI
|
||||
// =====================
|
||||
$nilaiDistribusi = Nilai::selectRaw('
|
||||
CASE
|
||||
WHEN (nilai_tugas + nilai_uts + nilai_uas)/3 >= 90 THEN "A"
|
||||
WHEN (nilai_tugas + nilai_uts + nilai_uas)/3 >= 80 THEN "B"
|
||||
WHEN (nilai_tugas + nilai_uts + nilai_uas)/3 >= 70 THEN "C"
|
||||
ELSE "D"
|
||||
END as grade,
|
||||
COUNT(*) as total
|
||||
')
|
||||
->groupBy('grade')
|
||||
->pluck('total', 'grade');
|
||||
|
||||
// =====================
|
||||
// RETURN VIEW
|
||||
// =====================
|
||||
return view('admin.dashboard', compact(
|
||||
'jumlahSiswa',
|
||||
'jumlahGuru',
|
||||
'jumlahKelas',
|
||||
'jumlahMapel',
|
||||
'siswaTerbaru',
|
||||
'nilaiTertinggi'
|
||||
'nilaiTertinggi',
|
||||
'kelasLabels',
|
||||
'kelasData',
|
||||
'nilaiDistribusi'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -72,10 +72,12 @@ public function store(Request $request)
|
|||
}
|
||||
|
||||
// ================= EDIT =================
|
||||
public function edit(Guru $guru)
|
||||
public function edit($id)
|
||||
{
|
||||
$guru = Guru::findOrFail($id);
|
||||
$mapel = MataPelajaran::all();
|
||||
return view('admin.guru.edit', compact('guru', 'mapel'));
|
||||
|
||||
return view('admin.guru.edit', compact('guru', 'mapel'));
|
||||
}
|
||||
|
||||
// ================= UPDATE =================
|
||||
|
|
@ -92,7 +94,7 @@ public function update(Request $request, Guru $guru)
|
|||
$guru->update([
|
||||
'nama' => $request->nama,
|
||||
'nip' => $request->nip,
|
||||
'mapel_id' => $request->mapel_id, // 🔥 FIX
|
||||
'mapel_id' => $request->mapel_id, // 🔥 INI WAJIB
|
||||
'alamat' => $request->alamat,
|
||||
'telepon' => $request->telepon,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -5,48 +5,78 @@
|
|||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class OtpController extends Controller
|
||||
{
|
||||
// ================= KIRIM OTP =================
|
||||
public function sendOtp(Request $request)
|
||||
{
|
||||
$user = User::where('email', $request->email)->first();
|
||||
$method = $request->input('method', 'email');
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'Email tidak ditemukan');
|
||||
if ($method === 'email') {
|
||||
// ── EMAIL ──
|
||||
$request->validate([
|
||||
'email' => 'required|email|exists:users,email',
|
||||
]);
|
||||
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
} else {
|
||||
// ── WHATSAPP ──
|
||||
$request->validate([
|
||||
'telepon' => 'required|string',
|
||||
]);
|
||||
|
||||
$no = preg_replace('/[^0-9]/', '', $request->telepon);
|
||||
if (str_starts_with($no, '0')) {
|
||||
$no = '62' . substr($no, 1);
|
||||
}
|
||||
$no08 = '0' . substr($no, 2);
|
||||
|
||||
$user = User::whereHas('guru', function ($q) use ($no, $no08) {
|
||||
$q->where('telepon', $no)
|
||||
->orWhere('telepon', $no08)
|
||||
->orWhere('telepon', 'like', '%' . substr($no, -9) . '%');
|
||||
})->orWhereHas('siswa', function ($q) use ($no, $no08) {
|
||||
$q->where('telepon', $no)
|
||||
->orWhere('telepon', $no08)
|
||||
->orWhere('telepon', 'like', '%' . substr($no, -9) . '%');
|
||||
})->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'Nomor WhatsApp tidak ditemukan.');
|
||||
}
|
||||
}
|
||||
|
||||
app(\App\Services\OtpService::class)->send($user);
|
||||
// Kirim OTP sesuai method (email SAJA atau WA SAJA)
|
||||
app(\App\Services\OtpService::class)->send($user, $method);
|
||||
|
||||
return back()->with('success', 'OTP berhasil dikirim');
|
||||
// Simpan email ke session untuk prefill form login
|
||||
session(['email' => $user->email]);
|
||||
|
||||
return back()->with('otp_sent', 'OTP berhasil dikirim!');
|
||||
}
|
||||
|
||||
// ================= LOGIN =================
|
||||
// ================= LOGIN DENGAN OTP =================
|
||||
public function loginOtp(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email',
|
||||
'email' => 'required|email',
|
||||
'password' => 'required',
|
||||
'otp' => 'nullable|digits:6'
|
||||
'otp' => 'nullable|digits:6',
|
||||
]);
|
||||
|
||||
$user = \App\Models\User::where('email', $request->email)->first();
|
||||
$user = User::where('email', $request->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
return back()->with('error', 'User tidak ditemukan');
|
||||
}
|
||||
|
||||
// 🔐 CEK PASSWORD
|
||||
if (!\Hash::check($request->password, $user->password)) {
|
||||
return back()->with('error', 'Password salah');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 🔥 KHUSUS GURU & SISWA WAJIB OTP
|
||||
// =========================
|
||||
// Guru & Siswa wajib OTP
|
||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
||||
|
||||
if (!$request->otp) {
|
||||
|
|
@ -57,7 +87,7 @@ public function loginOtp(Request $request)
|
|||
return back()->with('error', 'OTP belum diminta');
|
||||
}
|
||||
|
||||
if ((string)$request->otp !== (string)$user->otp) {
|
||||
if ((string) $request->otp !== (string) $user->otp) {
|
||||
return back()->with('error', 'OTP salah');
|
||||
}
|
||||
|
||||
|
|
@ -66,34 +96,21 @@ public function loginOtp(Request $request)
|
|||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// ✅ LOGIN
|
||||
// =========================
|
||||
\Auth::login($user);
|
||||
$request->session()->regenerate();
|
||||
|
||||
// 🔥 HAPUS OTP SETELAH DIPAKAI (hanya untuk guru & siswa)
|
||||
// Hapus OTP setelah dipakai
|
||||
if (in_array($user->role, ['guru', 'siswa'])) {
|
||||
$user->update([
|
||||
'otp' => null,
|
||||
'otp_expired_at' => null
|
||||
]);
|
||||
$user->update(['otp' => null, 'otp_expired_at' => null]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 🔁 REDIRECT ROLE
|
||||
// =========================
|
||||
if ($user->role == 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
session()->forget('email');
|
||||
|
||||
if ($user->role == 'guru') {
|
||||
return redirect()->route('guru.dashboard');
|
||||
}
|
||||
|
||||
if ($user->role == 'siswa') {
|
||||
return redirect()->route('siswa.dashboard');
|
||||
}
|
||||
|
||||
return redirect('/');
|
||||
return match ($user->role) {
|
||||
'admin' => redirect()->route('admin.dashboard'),
|
||||
'guru' => redirect()->route('guru.dashboard'),
|
||||
'siswa' => redirect()->route('siswa.dashboard'),
|
||||
default => redirect('/'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -9,122 +9,106 @@
|
|||
use App\Models\Kelas;
|
||||
use App\Models\MataPelajaran;
|
||||
use App\Models\Siswa;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class AbsensiController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$kelas = Kelas::all();
|
||||
$mapel = MataPelajaran::find(auth()->user()->guru->mapel_id);
|
||||
// 🔥 VALIDASI DI SINI
|
||||
if (!$mapel) {
|
||||
return back()->with('error', 'Mapel guru belum diatur!');
|
||||
}
|
||||
$siswa = null;
|
||||
$kelasAktif = null;
|
||||
$riwayat = null;
|
||||
public function index(Request $request)
|
||||
{
|
||||
$kelas = Kelas::all();
|
||||
$mapel = MataPelajaran::find(auth()->user()->guru->mapel_id);
|
||||
|
||||
if ($request->kelas_id && $request->mapel_id) {
|
||||
|
||||
$kelasAktif = Kelas::findOrFail($request->kelas_id);
|
||||
|
||||
$siswa = Siswa::where('kelas_id', $request->kelas_id)->get();
|
||||
|
||||
// 🔥 ambil absensi hari ini
|
||||
$absensi = Absensi::where('kelas_id', $request->kelas_id)
|
||||
->where('mapel_id', $request->mapel_id)
|
||||
->whereDate('tanggal', now())
|
||||
->first();
|
||||
|
||||
if ($absensi) {
|
||||
$riwayat = AbsensiDetail::with('siswa')
|
||||
->where('absensi_id', $absensi->id)
|
||||
->get();
|
||||
if (!$mapel) {
|
||||
return back()->with('error', 'Mapel guru belum diatur!');
|
||||
}
|
||||
|
||||
$siswa = null;
|
||||
$kelasAktif = null;
|
||||
$riwayat = null;
|
||||
|
||||
$aksi = $request->aksi;
|
||||
$tanggal = $request->tanggal ?? now()->toDateString();
|
||||
|
||||
if ($request->kelas_id && $request->mapel_id) {
|
||||
|
||||
// 🔍 MODE LIHAT RIWAYAT
|
||||
if ($aksi == 'lihat') {
|
||||
|
||||
$absensi = Absensi::where('kelas_id', $request->kelas_id)
|
||||
->where('mapel_id', $request->mapel_id)
|
||||
->whereDate('tanggal', $tanggal)
|
||||
->first();
|
||||
|
||||
if ($absensi) {
|
||||
$riwayat = AbsensiDetail::with('siswa')
|
||||
->where('absensi_id', $absensi->id)
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
||||
// ✏️ MODE INPUT ABSENSI
|
||||
if ($aksi == 'input') {
|
||||
|
||||
$kelasAktif = Kelas::findOrFail($request->kelas_id);
|
||||
|
||||
$siswa = Siswa::where('kelas_id', $request->kelas_id)->get();
|
||||
}
|
||||
}
|
||||
|
||||
return view('guru.absensi.index', compact(
|
||||
'kelas',
|
||||
'mapel',
|
||||
'siswa',
|
||||
'kelasAktif',
|
||||
'riwayat'
|
||||
));
|
||||
}
|
||||
|
||||
return view('guru.absensi.index', compact(
|
||||
'kelas',
|
||||
'mapel',
|
||||
'siswa',
|
||||
'kelasAktif',
|
||||
'riwayat'
|
||||
));
|
||||
}
|
||||
public function simpan(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'kelas_id' => 'required',
|
||||
'mapel_id' => 'required',
|
||||
'absensi' => 'required|array'
|
||||
]);
|
||||
|
||||
public function buka(Request $request)
|
||||
$absensi = Absensi::firstOrCreate([
|
||||
'kelas_id' => $request->kelas_id,
|
||||
'mapel_id' => $request->mapel_id,
|
||||
'tanggal' => $request->tanggal,
|
||||
], [
|
||||
'guru_id' => auth()->user()->guru->id
|
||||
]);
|
||||
|
||||
foreach ($request->absensi as $siswaId => $data) {
|
||||
|
||||
AbsensiDetail::updateOrCreate(
|
||||
[
|
||||
'absensi_id' => $absensi->id,
|
||||
'siswa_id' => $siswaId
|
||||
],
|
||||
[
|
||||
'status' => $data['status'],
|
||||
'keterangan' => $data['keterangan'] ?? null
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', 'Absensi berhasil disimpan');
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'kelas_id' => 'required',
|
||||
'mapel_id' => 'required'
|
||||
'status' => 'required'
|
||||
]);
|
||||
|
||||
$absensi = Absensi::create([
|
||||
'guru_id' => auth()->user()->guru->id,
|
||||
'kelas_id' => $request->kelas_id,
|
||||
'mapel_id' => $request->mapel_id,
|
||||
'tanggal' => now(),
|
||||
$detail = AbsensiDetail::findOrFail($id);
|
||||
|
||||
$detail->update([
|
||||
'status' => $request->status,
|
||||
'keterangan' => $request->keterangan
|
||||
]);
|
||||
|
||||
return redirect()->route('guru.absensi.kelola', $absensi->id);
|
||||
}
|
||||
|
||||
public function kelola($id)
|
||||
{
|
||||
$absensi = Absensi::findOrFail($id);
|
||||
|
||||
$siswa = Siswa::where('kelas_id', $absensi->kelas_id)->get();
|
||||
|
||||
return view('guru.absensi.kelola', compact('absensi', 'siswa'));
|
||||
}
|
||||
|
||||
public function simpan(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'kelas_id' => 'required',
|
||||
'mapel_id' => 'required',
|
||||
'absensi' => 'required|array'
|
||||
]);
|
||||
|
||||
// 🔥 CEK / BUAT absensi hari ini
|
||||
$absensi = Absensi::firstOrCreate([
|
||||
'kelas_id' => $request->kelas_id,
|
||||
'mapel_id' => $request->mapel_id,
|
||||
'tanggal' => now()->toDateString(),
|
||||
], [
|
||||
'guru_id' => auth()->user()->guru->id
|
||||
]);
|
||||
|
||||
// 🔥 SIMPAN DETAIL
|
||||
foreach ($request->absensi as $siswaId => $data) {
|
||||
|
||||
AbsensiDetail::updateOrCreate(
|
||||
[
|
||||
'absensi_id' => $absensi->id,
|
||||
'siswa_id' => $siswaId
|
||||
],
|
||||
[
|
||||
'status' => $data['status'],
|
||||
'keterangan' => $data['keterangan'] ?? null
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', 'Absensi berhasil disimpan');
|
||||
}
|
||||
|
||||
public function tutup($id)
|
||||
{
|
||||
Absensi::findOrFail($id)->update(['dibuka' => false]);
|
||||
|
||||
return back()->with('success', 'Absensi ditutup');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
AbsensiDetail::findOrFail($id)->delete();
|
||||
|
||||
return back()->with('success', 'Data dihapus');
|
||||
return back()->with('success', 'Absensi berhasil diupdate');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,34 @@
|
|||
|
||||
namespace App\Http\Controllers\guru;
|
||||
|
||||
use App\Models\Jadwal;
|
||||
use App\Models\Absensi;
|
||||
use App\Models\Tugas;
|
||||
use App\Models\Ujian;
|
||||
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('guru.dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$guruId = auth()->user()->guru_id;
|
||||
|
||||
$jumlahJadwal = Jadwal::where('guru_id', $guruId)->count();
|
||||
|
||||
$jumlahAbsensi = Absensi::where('guru_id', $guruId)->count();
|
||||
|
||||
$jumlahTugas = Tugas::where('guru_id', $guruId)->count();
|
||||
|
||||
$jumlahUjian = Ujian::where('guru_id', $guruId)->count();
|
||||
|
||||
return view('guru.dashboard', compact(
|
||||
'jumlahJadwal',
|
||||
'jumlahAbsensi',
|
||||
'jumlahTugas',
|
||||
'jumlahUjian'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,21 +15,41 @@
|
|||
|
||||
class TugasController extends Controller
|
||||
{
|
||||
public function index()
|
||||
public function index()
|
||||
{
|
||||
$guru = auth()->user()->guru;
|
||||
|
||||
$tugas = Tugas::with(['kelas', 'mapel'])
|
||||
->withCount('pengumpulan') // 🔥 hitung jumlah yang kumpul
|
||||
->withCount('pengumpulan')
|
||||
->where('guru_id', $guru->id)
|
||||
->get();
|
||||
|
||||
// 🔥 tambahkan total siswa per kelas
|
||||
foreach ($tugas as $t) {
|
||||
$t->total_siswa = Siswa::where('kelas_id', $t->kelas_id)->count();
|
||||
}
|
||||
|
||||
return view('guru.tugas.index', compact('tugas'));
|
||||
// Ambil semua pengumpulan dari semua tugas guru ini
|
||||
$tugasIds = $tugas->pluck('id');
|
||||
|
||||
$rekapNilai = \App\Models\PengumpulanTugas::with('siswa')
|
||||
->whereIn('tugas_id', $tugasIds)
|
||||
->whereNotNull('nilai')
|
||||
->get()
|
||||
->groupBy('siswa_id')
|
||||
->map(function($kumpulan) {
|
||||
$siswa = $kumpulan->first()->siswa;
|
||||
$nilaiList = $kumpulan->pluck('nilai')->map(fn($n) => (float)$n);
|
||||
$rata = round($nilaiList->avg(), 1);
|
||||
return [
|
||||
'nama' => $siswa->nama ?? '-',
|
||||
'inisial' => strtoupper(substr($siswa->nama ?? 'S', 0, 1)),
|
||||
'jumlah_tugas' => $kumpulan->count(),
|
||||
'rata_rata' => $rata,
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
||||
return view('guru.tugas.index', compact('tugas', 'rekapNilai'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
|
|
@ -145,15 +165,19 @@ public function pengumpulan($id)
|
|||
|
||||
foreach ($tugas->pengumpulan as $p) {
|
||||
$waktu = Carbon::parse($p->created_at);
|
||||
|
||||
if ($waktu->gt($deadline)) {
|
||||
$p->status = 'telat';
|
||||
} else {
|
||||
$p->status = 'tepat';
|
||||
}
|
||||
$p->status = $waktu->gt($deadline) ? 'telat' : 'tepat';
|
||||
}
|
||||
|
||||
return view('guru.tugas.pengumpulan', compact('tugas'));
|
||||
// Siapkan data untuk JS rata-rata
|
||||
$pengumpulanJson = $tugas->pengumpulan->map(function($p) {
|
||||
return [
|
||||
'nama' => $p->siswa->nama ?? '-',
|
||||
'inisial' => strtoupper(substr($p->siswa->nama ?? 'S', 0, 1)),
|
||||
'nilai' => $p->nilai,
|
||||
];
|
||||
});
|
||||
|
||||
return view('guru.tugas.pengumpulan', compact('tugas', 'pengumpulanJson'));
|
||||
}
|
||||
|
||||
public function nilai(Request $request, $id)
|
||||
|
|
|
|||
|
|
@ -6,92 +6,169 @@
|
|||
use Illuminate\Http\Request;
|
||||
use App\Models\Ujian;
|
||||
use App\Models\Soal;
|
||||
use App\Models\Hasil;
|
||||
use App\Models\Kelas;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UjianController extends Controller
|
||||
{
|
||||
public function index()
|
||||
// ── INDEX: daftar ujian + filter kelas untuk lihat nilai ──────────
|
||||
public function index(Request $request)
|
||||
{
|
||||
$data = Ujian::where('guru_id', auth()->id())->latest()->get();
|
||||
return view('guru.ujian.index', compact('data'));
|
||||
}
|
||||
$data = Ujian::where('guru_id', auth()->id())->latest()->get();
|
||||
$kelasList = Kelas::all();
|
||||
$selectedKelas = $request->kelas_id;
|
||||
|
||||
public function create()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
// safety check
|
||||
if (!$user) {
|
||||
abort(403, 'Belum login');
|
||||
// Nilai siswa: tampilkan kalau ada filter kelas
|
||||
$nilaiSiswa = collect();
|
||||
if ($selectedKelas) {
|
||||
$nilaiSiswa = Hasil::with(['siswa', 'ujian'])
|
||||
->whereHas('siswa', fn($q) => $q->where('kelas_id', $selectedKelas))
|
||||
->whereHas('ujian', fn($q) => $q->where('guru_id', auth()->id()))
|
||||
->get();
|
||||
}
|
||||
|
||||
$mapel = $user->mapel; // ✔ relasi sudah benar
|
||||
|
||||
return view('guru.ujian.create', compact('mapel'));
|
||||
return view('guru.ujian.index', compact('data', 'kelasList', 'selectedKelas', 'nilaiSiswa'));
|
||||
}
|
||||
|
||||
// ── CREATE ────────────────────────────────────────────────────────
|
||||
public function create()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (!$user) abort(403, 'Belum login');
|
||||
|
||||
$kelas = Kelas::all();
|
||||
$guru = $user->guru; // relasi user -> guru
|
||||
$mapel = $guru ? \App\Models\MataPelajaran::where('id', $guru->mapel_id)->get() : collect();
|
||||
|
||||
return view('guru.ujian.create', compact('kelas', 'mapel'));
|
||||
}
|
||||
|
||||
// ── STORE ─────────────────────────────────────────────────────────
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'judul' => 'required',
|
||||
'soal' => 'required|array'
|
||||
'judul' => 'required',
|
||||
'durasi' => 'required|integer',
|
||||
'mulai' => 'required|date',
|
||||
'selesai' => 'required|date|after:mulai',
|
||||
'kelas_ids'=> 'required|array|min:1',
|
||||
'mapel_id' => 'required',
|
||||
'jenis' => 'required|in:UTS,UAS',
|
||||
'soal' => 'required|array|min:1',
|
||||
'soal.*.pertanyaan' => 'required',
|
||||
'soal.*.a' => 'required',
|
||||
'soal.*.b' => 'required',
|
||||
'soal.*.c' => 'required',
|
||||
'soal.*.d' => 'required',
|
||||
'soal.*.jawaban' => 'required|in:a,b,c,d',
|
||||
]);
|
||||
|
||||
// 🔥 SIMPAN UJIAN
|
||||
$ujian = Ujian::create([
|
||||
'judul' => $request->judul,
|
||||
'durasi' => $request->durasi,
|
||||
'mulai' => $request->mulai,
|
||||
'selesai' => $request->selesai,
|
||||
'guru_id' => auth()->user()->guru_id
|
||||
]);
|
||||
|
||||
// 🔥 SIMPAN SOAL
|
||||
foreach ($request->soal as $s) {
|
||||
Soal::create([
|
||||
'ujian_id' => $ujian->id,
|
||||
'pertanyaan' => $s['pertanyaan'],
|
||||
'a' => $s['a'],
|
||||
'b' => $s['b'],
|
||||
'c' => $s['c'],
|
||||
'd' => $s['d'],
|
||||
'jawaban_benar' => $s['jawaban'],
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$ujian = Ujian::create([
|
||||
'judul' => $request->judul,
|
||||
'durasi' => $request->durasi,
|
||||
'mulai' => $request->mulai,
|
||||
'selesai' => $request->selesai,
|
||||
'guru_id' => auth()->id(),
|
||||
'mapel_id' => $request->mapel_id,
|
||||
'jenis' => $request->jenis,
|
||||
'status_kirim' => 'draft',
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('guru.ujian.index')
|
||||
->with('success', 'Ujian + soal berhasil dibuat');
|
||||
// Simpan pivot kelas (multi)
|
||||
$ujian->kelas()->sync($request->kelas_ids);
|
||||
|
||||
// Simpan soal
|
||||
foreach ($request->soal as $s) {
|
||||
Soal::create([
|
||||
'ujian_id' => $ujian->id,
|
||||
'pertanyaan' => $s['pertanyaan'],
|
||||
'a' => $s['a'],
|
||||
'b' => $s['b'],
|
||||
'c' => $s['c'],
|
||||
'd' => $s['d'],
|
||||
'jawaban_benar' => $s['jawaban'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
return redirect()->route('guru.ujian.index')
|
||||
->with('success', 'Ujian berhasil dibuat. Kirim ke siswa saat siap.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return back()->with('error', 'Gagal: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── KIRIM ke semua kelas (ubah status_kirim → terkirim) ──────────
|
||||
public function kirim($id)
|
||||
{
|
||||
$ujian = Ujian::where('guru_id', auth()->id())->findOrFail($id);
|
||||
$ujian->update(['status_kirim' => 'terkirim']);
|
||||
|
||||
return redirect()->route('guru.ujian.index')
|
||||
->with('success', 'Ujian "' . $ujian->judul . '" berhasil dikirim ke siswa!');
|
||||
}
|
||||
|
||||
// ── EDIT ─────────────────────────────────────────────────────────
|
||||
public function edit($id)
|
||||
{
|
||||
$ujian = Ujian::where('guru_id', auth()->id())->with('kelas')->findOrFail($id);
|
||||
$kelas = Kelas::all();
|
||||
$mapel = \App\Models\MataPelajaran::all();
|
||||
$selectedKelas = $ujian->kelas->pluck('id')->toArray();
|
||||
|
||||
return view('guru.ujian.edit', compact('ujian', 'kelas', 'mapel', 'selectedKelas'));
|
||||
}
|
||||
|
||||
// ── UPDATE ────────────────────────────────────────────────────────
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'judul' => 'required',
|
||||
'durasi' => 'required|integer',
|
||||
'mulai' => 'required|date',
|
||||
'selesai' => 'required|date|after:mulai',
|
||||
'kelas_ids' => 'required|array|min:1',
|
||||
'mapel_id' => 'required',
|
||||
'jenis' => 'required|in:UTS,UAS',
|
||||
]);
|
||||
|
||||
$ujian = Ujian::where('guru_id', auth()->id())->findOrFail($id);
|
||||
$ujian->update([
|
||||
'judul' => $request->judul,
|
||||
'durasi' => $request->durasi,
|
||||
'mulai' => $request->mulai,
|
||||
'selesai' => $request->selesai,
|
||||
'mapel_id' => $request->mapel_id,
|
||||
'jenis' => $request->jenis,
|
||||
]);
|
||||
$ujian->kelas()->sync($request->kelas_ids);
|
||||
|
||||
return redirect()->route('guru.ujian.index')
|
||||
->with('success', 'Ujian berhasil diupdate');
|
||||
}
|
||||
|
||||
// ── DESTROY ───────────────────────────────────────────────────────
|
||||
public function destroy($id)
|
||||
{
|
||||
$ujian = Ujian::where('guru_id', auth()->id())->findOrFail($id);
|
||||
Soal::where('ujian_id', $ujian->id)->delete();
|
||||
$ujian->kelas()->detach();
|
||||
$ujian->delete();
|
||||
|
||||
return redirect()->route('guru.ujian.index')
|
||||
->with('success', 'Ujian berhasil dihapus');
|
||||
}
|
||||
|
||||
// ── SOAL (opsional, halaman terpisah) ────────────────────────────
|
||||
public function soal($id)
|
||||
{
|
||||
$ujian = Ujian::findOrFail($id);
|
||||
return view('guru.ujian.soal', compact('ujian'));
|
||||
}
|
||||
|
||||
public function storeSoal(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'soal' => 'required|array',
|
||||
'soal.*.pertanyaan' => 'required|string',
|
||||
'soal.*.a' => 'required|string',
|
||||
'soal.*.b' => 'required|string',
|
||||
'soal.*.c' => 'required|string',
|
||||
'soal.*.d' => 'required|string',
|
||||
'soal.*.jawaban' => 'required|in:a,b,c,d',
|
||||
]);
|
||||
|
||||
foreach ($request->soal as $s) {
|
||||
Soal::create([
|
||||
'ujian_id' => $id,
|
||||
'pertanyaan' => $s['pertanyaan'],
|
||||
'a' => $s['a'],
|
||||
'b' => $s['b'],
|
||||
'c' => $s['c'],
|
||||
'd' => $s['d'],
|
||||
'jawaban_benar' => $s['jawaban'],
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success','Soal disimpan');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,44 @@
|
|||
|
||||
namespace App\Http\Controllers\Siswa;
|
||||
|
||||
use App\Models\Nilai;
|
||||
use App\Models\AbsensiDetail;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('siswa.dashboard');
|
||||
$user = auth()->user();
|
||||
|
||||
$nilai = \App\Models\Nilai::where('siswa_id', $user->siswa_id)->get();
|
||||
|
||||
$rataNilai = $nilai->avg('nilai');
|
||||
|
||||
$nilaiTerbaru = \App\Models\Nilai::with('mapel')
|
||||
->where('siswa_id', $user->siswa_id)
|
||||
->latest()
|
||||
->limit(5)
|
||||
->get();
|
||||
|
||||
$absensiTerakhir = \App\Models\AbsensiDetail::where('siswa_id', $user->siswa_id)
|
||||
->latest()
|
||||
->limit(5)
|
||||
->get();
|
||||
|
||||
$hadir = \App\Models\AbsensiDetail::where('siswa_id', $user->siswa_id)
|
||||
->where('status', 'hadir')
|
||||
->count();
|
||||
|
||||
$tugas = 0;
|
||||
|
||||
return view('siswa.dashboard', compact(
|
||||
'rataNilai',
|
||||
'nilaiTerbaru',
|
||||
'absensiTerakhir',
|
||||
'hadir',
|
||||
'tugas'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -9,13 +9,86 @@
|
|||
class NilaiController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
{
|
||||
$siswa_id = auth()->user()->siswa_id;
|
||||
|
||||
$nilai = Nilai::with(['mapel'])
|
||||
->where('siswa_id', $user->siswa_id)
|
||||
->get();
|
||||
// 🔹 Ambil nilai tugas
|
||||
$tugas = \App\Models\PengumpulanTugas::with('tugas.mapel')
|
||||
->where('siswa_id', $siswa_id)
|
||||
->get();
|
||||
|
||||
return view('siswa.nilai.index', compact('nilai'));
|
||||
// 🔹 Ambil nilai ujian
|
||||
$ujian = \App\Models\Hasil::with('ujian.mapel')
|
||||
->where('siswa_id', $siswa_id)
|
||||
->get();
|
||||
|
||||
// 🔥 REKAP DATA
|
||||
$data = [];
|
||||
|
||||
// =======================
|
||||
// 🔹 PROSES NILAI TUGAS
|
||||
// =======================
|
||||
foreach ($tugas as $t) {
|
||||
$mapel_id = $t->tugas->mapel->id ?? null;
|
||||
|
||||
if (!$mapel_id) continue;
|
||||
|
||||
if (!isset($data[$mapel_id])) {
|
||||
$data[$mapel_id] = [
|
||||
'mapel' => $t->tugas->mapel->nama_mapel,
|
||||
'tugas' => [],
|
||||
'uts' => null,
|
||||
'uas' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$data[$mapel_id]['tugas'][] = $t->nilai;
|
||||
}
|
||||
|
||||
// =======================
|
||||
// 🔹 PROSES NILAI UJIAN
|
||||
// =======================
|
||||
foreach ($ujian as $u) {
|
||||
$mapel_id = $u->ujian->mapel->id ?? null;
|
||||
|
||||
if (!$mapel_id) continue;
|
||||
|
||||
if (!isset($data[$mapel_id])) {
|
||||
$data[$mapel_id] = [
|
||||
'mapel' => $u->ujian->mapel->nama_mapel,
|
||||
'tugas' => [],
|
||||
'uts' => null,
|
||||
'uas' => null,
|
||||
];
|
||||
}
|
||||
|
||||
if ($u->ujian->jenis == 'UTS') {
|
||||
$data[$mapel_id]['uts'] = $u->nilai;
|
||||
}
|
||||
|
||||
if ($u->ujian->jenis == 'UAS') {
|
||||
$data[$mapel_id]['uas'] = $u->nilai;
|
||||
}
|
||||
}
|
||||
|
||||
// =======================
|
||||
// 🔹 HITUNG RATA-RATA
|
||||
// =======================
|
||||
foreach ($data as $k => $d) {
|
||||
|
||||
$rata_tugas = count($d['tugas'])
|
||||
? array_sum($d['tugas']) / count($d['tugas'])
|
||||
: 0;
|
||||
|
||||
$uts = $d['uts'] ?? 0;
|
||||
$uas = $d['uas'] ?? 0;
|
||||
|
||||
$rata = ($rata_tugas + $uts + $uas) / 3;
|
||||
|
||||
$data[$k]['tugas'] = round($rata_tugas);
|
||||
$data[$k]['rata'] = round($rata);
|
||||
}
|
||||
|
||||
return view('siswa.nilai.index', compact('data'));
|
||||
}
|
||||
}
|
||||
|
|
@ -6,47 +6,84 @@
|
|||
use Illuminate\Http\Request;
|
||||
use App\Models\Ujian;
|
||||
use App\Models\Soal;
|
||||
use App\Models\Jawaban;
|
||||
use App\Models\Hasil;
|
||||
|
||||
class UjianController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$ujians = Ujian::all();
|
||||
return view('siswa.ujian.index', compact('ujians'));
|
||||
}
|
||||
public function index()
|
||||
{
|
||||
$siswa = auth()->user()->siswa;
|
||||
|
||||
// Hanya tampilkan ujian yang dikirim ke kelas siswa ini & status terkirim
|
||||
$ujians = Ujian::whereHas('kelas', fn($q) => $q->where('kelas.id', $siswa->kelas_id))
|
||||
->where('status_kirim', 'terkirim')
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$sudahDikerjakan = Hasil::where('siswa_id', $siswa->id)
|
||||
->pluck('ujian_id')->toArray();
|
||||
|
||||
$hasilSiswa = Hasil::where('siswa_id', $siswa->id)
|
||||
->get()->keyBy('ujian_id');
|
||||
|
||||
return view('siswa.ujian.index', compact('ujians', 'sudahDikerjakan', 'hasilSiswa'));
|
||||
}
|
||||
|
||||
public function kerjakan($id)
|
||||
{
|
||||
$siswa = auth()->user()->siswa;
|
||||
|
||||
$sudah = Hasil::where('siswa_id', $siswa->id)
|
||||
->where('ujian_id', $id)
|
||||
->exists();
|
||||
|
||||
if ($sudah) {
|
||||
return redirect()->route('siswa.ujian.index')
|
||||
->with('warning', 'Kamu sudah mengerjakan ujian ini.');
|
||||
}
|
||||
|
||||
$ujian = Ujian::findOrFail($id);
|
||||
$soals = Soal::where('ujian_id', $id)->get();
|
||||
|
||||
return view('siswa.ujian.kerjakan', compact('ujian','soals'));
|
||||
return view('siswa.ujian.kerjakan', compact('ujian', 'soals'));
|
||||
}
|
||||
public function submit(Request $request, $id)
|
||||
{
|
||||
$soals = Soal::where('ujian_id', $id)->get();
|
||||
|
||||
$benar = 0;
|
||||
$total = $soals->count();
|
||||
public function submit(Request $request, $id)
|
||||
{
|
||||
$siswa = auth()->user()->siswa;
|
||||
|
||||
foreach ($soals as $s) {
|
||||
$jawabanUser = $request->jawaban[$s->id] ?? null;
|
||||
// Cegah submit ulang
|
||||
$sudah = Hasil::where('siswa_id', $siswa->id)
|
||||
->where('ujian_id', $id)
|
||||
->exists();
|
||||
|
||||
if ($jawabanUser == $s->jawaban_benar) {
|
||||
$benar++;
|
||||
if ($sudah) {
|
||||
return redirect()->route('siswa.ujian.index')
|
||||
->with('warning', 'Kamu sudah mengerjakan ujian ini.');
|
||||
}
|
||||
|
||||
$soals = Soal::where('ujian_id', $id)->get();
|
||||
$ujian = Ujian::findOrFail($id);
|
||||
|
||||
$benar = 0;
|
||||
$total = $soals->count();
|
||||
|
||||
foreach ($soals as $s) {
|
||||
$jawabanUser = $request->jawaban[$s->id] ?? null;
|
||||
if ($jawabanUser == $s->jawaban_benar) {
|
||||
$benar++;
|
||||
}
|
||||
}
|
||||
|
||||
$nilai = round(($benar / $total) * 100);
|
||||
|
||||
// Simpan ke tabel hasils (hanya kolom yang ada)
|
||||
Hasil::create([
|
||||
'siswa_id' => $siswa->id,
|
||||
'ujian_id' => $id,
|
||||
'nilai' => $nilai,
|
||||
]);
|
||||
|
||||
return view('siswa.ujian.hasil', compact('nilai', 'benar', 'total', 'ujian'));
|
||||
}
|
||||
|
||||
$nilai = ($benar / $total) * 100;
|
||||
|
||||
return view('siswa.ujian.hasil', [
|
||||
'nilai' => $nilai,
|
||||
'benar' => $benar,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CekStatusUser
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user && $user->guru && $user->guru->status !== 'aktif') {
|
||||
auth()->logout();
|
||||
|
||||
return redirect('/login')
|
||||
->with('error', 'Akun anda dinonaktifkan');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,12 +11,13 @@ class AbsensiDetail extends Model
|
|||
|
||||
protected $table = 'absensi_detail';
|
||||
|
||||
protected $fillable = [
|
||||
'absensi_id', // 🔥 WAJIB
|
||||
'siswa_id', // 🔥 WAJIB
|
||||
'status', // 🔥 hadir / izin / alpha
|
||||
'waktu_absen' // 🔥 timestamp
|
||||
];
|
||||
protected $fillable = [
|
||||
'absensi_id',
|
||||
'siswa_id',
|
||||
'status',
|
||||
'keterangan', // 🔥 TAMBAHKAN INI
|
||||
'waktu_absen'
|
||||
];
|
||||
|
||||
public function absensi()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,15 +2,19 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Hasil extends Model
|
||||
{
|
||||
protected $fillable = ['user_id','ujian_id','nilai'];
|
||||
protected $fillable = ['siswa_id', 'ujian_id', 'nilai'];
|
||||
|
||||
public function ujian()
|
||||
{
|
||||
return $this->belongsTo(Ujian::class);
|
||||
return $this->belongsTo(\App\Models\Ujian::class, 'ujian_id');
|
||||
}
|
||||
}
|
||||
|
||||
public function siswa()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Siswa::class, 'siswa_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,42 @@ class Nilai extends Model
|
|||
{
|
||||
protected $table = 'nilai';
|
||||
|
||||
protected $fillable = ['siswa_id', 'mapel_id', 'nilai'];
|
||||
protected $fillable = [
|
||||
'siswa_id',
|
||||
'mapel_id',
|
||||
|
||||
// 🔥 lama (jangan dihapus dulu)
|
||||
'nilai',
|
||||
|
||||
// 🔥 baru
|
||||
'nilai_tugas',
|
||||
'nilai_uts',
|
||||
'nilai_uas'
|
||||
];
|
||||
|
||||
public function siswa()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Siswa::class);
|
||||
}
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Siswa::class);
|
||||
}
|
||||
|
||||
public function mapel()
|
||||
{
|
||||
return $this->belongsTo(MataPelajaran::class);
|
||||
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
||||
}
|
||||
|
||||
// 🔥 helper biar fleksibel (INI KUNCI AMAN)
|
||||
public function getNilaiAkhirAttribute()
|
||||
{
|
||||
// kalau pakai sistem baru
|
||||
if ($this->nilai_tugas || $this->nilai_uts || $this->nilai_uas) {
|
||||
return collect([
|
||||
$this->nilai_tugas,
|
||||
$this->nilai_uts,
|
||||
$this->nilai_uas
|
||||
])->filter()->avg();
|
||||
}
|
||||
|
||||
// fallback ke sistem lama
|
||||
return $this->nilai;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,6 @@ public function siswa()
|
|||
|
||||
public function tugas()
|
||||
{
|
||||
return $this->belongsTo(Tugas::class);
|
||||
return $this->belongsTo(\App\Models\Tugas::class, 'tugas_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public function kelas()
|
|||
|
||||
public function mapel()
|
||||
{
|
||||
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
||||
return $this->belongsTo(\App\Models\MataPelajaran::class, 'mapel_id');
|
||||
}
|
||||
|
||||
public function guru()
|
||||
|
|
|
|||
|
|
@ -2,19 +2,43 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Ujian extends Model
|
||||
{ protected $fillable = ['judul','durasi','mulai','selesai','guru_id'];
|
||||
{
|
||||
protected $fillable = [
|
||||
'judul', 'durasi', 'mulai', 'selesai',
|
||||
'guru_id', 'mapel_id', 'jenis', 'status_kirim',
|
||||
];
|
||||
|
||||
public function soals()
|
||||
// ── Relasi ke banyak kelas (pivot ujian_kelas) ─────────────────────
|
||||
// Satu ujian bisa dikirim ke banyak kelas
|
||||
public function kelas()
|
||||
{
|
||||
return $this->hasMany(Soal::class);
|
||||
return $this->belongsToMany(Kelas::class, 'ujian_kelas', 'ujian_id', 'kelas_id');
|
||||
}
|
||||
|
||||
// ── Relasi ke soal ────────────────────────────────────────────────
|
||||
public function soals()
|
||||
{
|
||||
return $this->hasMany(Soal::class, 'ujian_id');
|
||||
}
|
||||
|
||||
// ── Relasi ke guru ────────────────────────────────────────────────
|
||||
public function guru()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'guru_id');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Relasi ke mapel ───────────────────────────────────────────────
|
||||
public function mapel()
|
||||
{
|
||||
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
||||
}
|
||||
|
||||
// ── Relasi ke hasil (nilai) ───────────────────────────────────────
|
||||
public function hasils()
|
||||
{
|
||||
return $this->hasMany(Hasil::class, 'ujian_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +94,8 @@ public function mapel()
|
|||
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 🔥 HELPER TAMBAHAN (OPSIONAL TAPI BAGUS)
|
||||
|
|
|
|||
|
|
@ -9,64 +9,70 @@
|
|||
|
||||
class OtpService
|
||||
{
|
||||
public function send(User $user)
|
||||
{
|
||||
$otp = rand(100000, 999999);
|
||||
/**
|
||||
* Kirim OTP ke email SAJA atau WA SAJA, tidak keduanya.
|
||||
* $method = 'email' | 'wa'
|
||||
*/
|
||||
public function send(User $user, string $method = 'email')
|
||||
{
|
||||
$otp = rand(100000, 999999);
|
||||
|
||||
// simpan OTP
|
||||
$user->update([
|
||||
'otp' => $otp,
|
||||
'otp_expired_at' => now()->addMinutes(5)
|
||||
]);
|
||||
|
||||
// ================= EMAIL =================
|
||||
\Mail::raw("Kode OTP kamu: $otp", function ($msg) use ($user) {
|
||||
$msg->to($user->email)
|
||||
->subject('Kode OTP Login');
|
||||
});
|
||||
|
||||
// ================= AMBIL TELEPON =================
|
||||
$telepon = null;
|
||||
|
||||
if ($user->siswa && $user->siswa->telepon) {
|
||||
$telepon = $user->siswa->telepon;
|
||||
} elseif ($user->guru && $user->guru->telepon) {
|
||||
$telepon = $user->guru->telepon;
|
||||
} elseif ($user->telepon) {
|
||||
$telepon = $user->telepon;
|
||||
}
|
||||
|
||||
// ================= KIRIM WA =================
|
||||
if ($telepon) {
|
||||
$this->sendWA($telepon, $otp);
|
||||
} else {
|
||||
\Log::error('NO HP TIDAK ADA UNTUK USER', [
|
||||
'user_id' => $user->id,
|
||||
'role' => $user->role
|
||||
// Simpan OTP ke database
|
||||
$user->update([
|
||||
'otp' => $otp,
|
||||
'otp_expired_at' => now()->addMinutes(5),
|
||||
]);
|
||||
|
||||
if ($method === 'email') {
|
||||
// ── KIRIM KE EMAIL SAJA ──────────────────────
|
||||
\Mail::raw(
|
||||
"Kode OTP kamu: {$otp}\nBerlaku 5 menit. Jangan bagikan ke siapapun.",
|
||||
function ($msg) use ($user) {
|
||||
$msg->to($user->email)->subject('Kode OTP Login');
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
// ── KIRIM KE WA SAJA ─────────────────────────
|
||||
$telepon = null;
|
||||
|
||||
if ($user->siswa && $user->siswa->telepon) {
|
||||
$telepon = $user->siswa->telepon;
|
||||
} elseif ($user->guru && $user->guru->telepon) {
|
||||
$telepon = $user->guru->telepon;
|
||||
} elseif ($user->telepon) {
|
||||
$telepon = $user->telepon;
|
||||
}
|
||||
|
||||
if ($telepon) {
|
||||
$this->sendWA($telepon, $otp);
|
||||
} else {
|
||||
Log::error('NO HP TIDAK ADA UNTUK USER', [
|
||||
'user_id' => $user->id,
|
||||
'role' => $user->role,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sendWA($no, $otp)
|
||||
{
|
||||
$no = preg_replace('/[^0-9]/', '', $no);
|
||||
|
||||
if (substr($no, 0, 1) === '0') {
|
||||
if (str_starts_with($no, '0')) {
|
||||
$no = '62' . substr($no, 1);
|
||||
}
|
||||
|
||||
$token = env('FONNTE_TOKEN');
|
||||
|
||||
$response = Http::asForm()->withHeaders([
|
||||
'Authorization' => $token
|
||||
'Authorization' => env('FONNTE_TOKEN'),
|
||||
])->post('https://api.fonnte.com/send', [
|
||||
'target' => $no,
|
||||
'message' => "Kode OTP kamu: $otp"
|
||||
'target' => $no,
|
||||
'message' => "🔐 Kode OTP kamu: *{$otp}*\nBerlaku 5 menit. Jangan bagikan ke siapapun.",
|
||||
]);
|
||||
|
||||
Log::info('WA RESULT', [
|
||||
'nomor' => $no,
|
||||
'response' => $response->json()
|
||||
'nomor' => $no,
|
||||
'response' => $response->json(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,11 +11,12 @@
|
|||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function ($middleware) {
|
||||
$middleware->alias([
|
||||
'force.password' => ForcePasswordChange::class,
|
||||
]);
|
||||
})
|
||||
->withMiddleware(function ($middleware) {
|
||||
$middleware->alias([
|
||||
'force.password' => \App\Http\Middleware\ForcePasswordChange::class,
|
||||
'cek.status' => \App\Http\Middleware\CekStatusUser::class,
|
||||
]);
|
||||
})
|
||||
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('nilai', function (Blueprint $table) {
|
||||
$table->integer('nilai_tugas')->nullable();
|
||||
$table->integer('nilai_uts')->nullable();
|
||||
$table->integer('nilai_uas')->nullable();
|
||||
|
||||
// optional: hapus kolom lama
|
||||
$table->dropColumn('nilai');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('nilai', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?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
|
||||
{
|
||||
// ── 1. Tambah kolom ke tabel ujians ──────────────────────────────
|
||||
Schema::table('ujians', function (Blueprint $table) {
|
||||
// mapel_id: guru mengajar mapel apa
|
||||
if (!Schema::hasColumn('ujians', 'mapel_id')) {
|
||||
$table->unsignedBigInteger('mapel_id')->nullable()->after('guru_id');
|
||||
}
|
||||
// jenis: UTS atau UAS
|
||||
if (!Schema::hasColumn('ujians', 'jenis')) {
|
||||
$table->enum('jenis', ['UTS', 'UAS'])->default('UTS')->after('mapel_id');
|
||||
}
|
||||
// status_kirim: draft = belum dikirim ke siswa, terkirim = sudah dikirim
|
||||
if (!Schema::hasColumn('ujians', 'status_kirim')) {
|
||||
$table->enum('status_kirim', ['draft', 'terkirim'])->default('draft')->after('jenis');
|
||||
}
|
||||
});
|
||||
|
||||
// ── 2. Buat tabel pivot ujian_kelas ──────────────────────────────
|
||||
// Satu ujian bisa dikirim ke BANYAK kelas sekaligus
|
||||
if (!Schema::hasTable('ujian_kelas')) {
|
||||
Schema::create('ujian_kelas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('ujian_id');
|
||||
$table->unsignedBigInteger('kelas_id');
|
||||
$table->timestamps();
|
||||
|
||||
// Satu ujian tidak boleh duplikat ke kelas yang sama
|
||||
$table->unique(['ujian_id', 'kelas_id']);
|
||||
|
||||
$table->foreign('ujian_id')->references('id')->on('ujians')->onDelete('cascade');
|
||||
$table->foreign('kelas_id')->references('id')->on('kelas')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('ujians', function (Blueprint $table) {
|
||||
$table->dropColumn(['mapel_id', 'jenis', 'status_kirim']);
|
||||
});
|
||||
|
||||
Schema::dropIfExists('ujian_kelas');
|
||||
}
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
|
|
@ -1,80 +1,666 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Dashboard Admin</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
|
||||
:root {
|
||||
--brand-primary: #1a56db;
|
||||
--brand-primary-light: #e8f0fe;
|
||||
--brand-success: #0d9488;
|
||||
--brand-success-light: #ccfbf1;
|
||||
--brand-warning: #d97706;
|
||||
--brand-warning-light: #fef3c7;
|
||||
--brand-danger: #dc2626;
|
||||
--brand-danger-light: #fee2e2;
|
||||
--surface-base: #ffffff;
|
||||
--surface-page: #f4f6fa;
|
||||
--surface-raised: #ffffff;
|
||||
--border-soft: rgba(0,0,0,0.07);
|
||||
--border-medium: rgba(0,0,0,0.12);
|
||||
--text-primary: #111827;
|
||||
--text-secondary: #6b7280;
|
||||
--text-muted: #9ca3af;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 20px;
|
||||
--shadow-xs: 0 1px 2px rgba(0,0,0,0.04);
|
||||
--shadow-sm: 0 1px 4px rgba(0,0,0,0.06), 0 2px 8px rgba(0,0,0,0.04);
|
||||
--shadow-md: 0 4px 16px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
body, .dashboard-wrap * {
|
||||
|
||||
}
|
||||
|
||||
.dashboard-wrap {
|
||||
background: var(--surface-page);
|
||||
min-height: 100vh;
|
||||
padding: 28px 32px 48px;
|
||||
}
|
||||
|
||||
/* ─── HEADER ─── */
|
||||
.db-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.db-header-left .db-eyebrow {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--brand-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.db-header-left h1 {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.db-user-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: var(--surface-base);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: 40px;
|
||||
padding: 6px 14px 6px 8px;
|
||||
box-shadow: var(--shadow-xs);
|
||||
}
|
||||
|
||||
.db-user-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #1a56db 0%, #6366f1 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.db-user-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* ─── STAT CARDS ─── */
|
||||
.db-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--surface-base);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border-soft);
|
||||
padding: 20px 22px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
}
|
||||
|
||||
.stat-card.primary::before { background: var(--brand-primary); }
|
||||
.stat-card.success::before { background: var(--brand-success); }
|
||||
.stat-card.warning::before { background: var(--brand-warning); }
|
||||
.stat-card.danger::before { background: var(--brand-danger); }
|
||||
|
||||
.stat-card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.stat-card-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: var(--radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.stat-icon.primary { background: var(--brand-primary-light); color: var(--brand-primary); }
|
||||
.stat-icon.success { background: var(--brand-success-light); color: var(--brand-success); }
|
||||
.stat-icon.warning { background: var(--brand-warning-light); color: var(--brand-warning); }
|
||||
.stat-icon.danger { background: var(--brand-danger-light); color: var(--brand-danger); }
|
||||
|
||||
.stat-value {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -1.5px;
|
||||
line-height: 1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: 'DM Mono', monospace;
|
||||
}
|
||||
|
||||
/* ─── SECTION LABEL ─── */
|
||||
.section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
/* ─── CARD BASE ─── */
|
||||
.db-card {
|
||||
background: var(--surface-base);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border-soft);
|
||||
box-shadow: var(--shadow-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.db-card-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.db-card-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: -0.2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.db-card-title .title-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.db-card-title .title-dot.primary { background: var(--brand-primary); }
|
||||
.db-card-title .title-dot.teal { background: var(--brand-success); }
|
||||
|
||||
.db-card-badge {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 3px 8px;
|
||||
border-radius: 20px;
|
||||
background: var(--surface-page);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-soft);
|
||||
}
|
||||
|
||||
.db-card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* ─── CHARTS GRID ─── */
|
||||
.charts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* ─── DATA LISTS ─── */
|
||||
.data-lists-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.data-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 11px 20px;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.data-row:last-child { border-bottom: none; }
|
||||
|
||||
.data-row:hover { background: var(--surface-page); }
|
||||
|
||||
.data-row-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.data-row-index {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
font-family: 'DM Mono', monospace;
|
||||
min-width: 18px;
|
||||
}
|
||||
|
||||
.data-row-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.data-row-sub {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
background: var(--surface-page);
|
||||
border: 1px solid var(--border-soft);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.score-badge {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
font-family: 'DM Mono', monospace;
|
||||
color: #fff;
|
||||
background: var(--brand-success);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 3px 10px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.score-badge.high { background: #0d9488; }
|
||||
.score-badge.mid { background: #d97706; }
|
||||
.score-badge.low { background: #dc2626; }
|
||||
|
||||
/* ─── QUICK ACTIONS ─── */
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: var(--surface-base);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border-soft);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: 18px 24px;
|
||||
}
|
||||
|
||||
.qa-left h5 {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 2px;
|
||||
}
|
||||
|
||||
.qa-left small {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.qa-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-qa {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 9px 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
border: 1.5px solid;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: all 0.15s ease;
|
||||
letter-spacing: 0.01em;
|
||||
|
||||
}
|
||||
|
||||
.btn-qa:hover { transform: translateY(-1px); filter: brightness(0.95); }
|
||||
|
||||
.btn-qa.primary {
|
||||
background: var(--brand-primary);
|
||||
border-color: var(--brand-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-qa.success {
|
||||
background: var(--brand-success);
|
||||
border-color: var(--brand-success);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-qa.outline {
|
||||
background: transparent;
|
||||
border-color: var(--border-medium);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* ─── EMPTY STATE ─── */
|
||||
.empty-state {
|
||||
padding: 28px 20px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="dashboard-wrap">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="db-header">
|
||||
<div class="db-header-left">
|
||||
<div class="db-eyebrow">Panel Admin</div>
|
||||
<h1>Dashboard</h1>
|
||||
</div>
|
||||
|
||||
<div class="db-user-badge">
|
||||
<div class="db-user-avatar">
|
||||
{{ strtoupper(substr(auth()->user()->name, 0, 2)) }}
|
||||
</div>
|
||||
<span class="db-user-name">{{ auth()->user()->name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATISTIK --}}
|
||||
<div class="row">
|
||||
@php
|
||||
$cards = [
|
||||
['title' => 'Total Siswa', 'value' => $jumlahSiswa, 'color' => 'primary', 'icon' => 'fa-solid fa-user-graduate'],
|
||||
['title' => 'Total Guru', 'value' => $jumlahGuru, 'color' => 'success', 'icon' => 'fa-solid fa-chalkboard-teacher'],
|
||||
['title' => 'Total Kelas', 'value' => $jumlahKelas, 'color' => 'warning', 'icon' => 'fa-solid fa-school'],
|
||||
['title' => 'Mata Pelajaran', 'value' => $jumlahMapel, 'color' => 'danger', 'icon' => 'fa-solid fa-book'],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card shadow border-left-primary">
|
||||
<div class="card-body">
|
||||
<h6>Jumlah Siswa</h6>
|
||||
<h4>{{ $jumlahSiswa }}</h4>
|
||||
<div class="db-stats-grid">
|
||||
@foreach($cards as $c)
|
||||
<div class="stat-card {{ $c['color'] }}">
|
||||
<div class="stat-card-top">
|
||||
<span class="stat-card-label">{{ $c['title'] }}</span>
|
||||
<div class="stat-icon {{ $c['color'] }}">
|
||||
<i class="{{ $c['icon'] }}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-value">{{ number_format($c['value']) }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- CHARTS --}}
|
||||
<p class="section-label">Visualisasi Data</p>
|
||||
<div class="charts-grid">
|
||||
|
||||
{{-- CHART SISWA PER KELAS --}}
|
||||
<div class="db-card">
|
||||
<div class="db-card-header">
|
||||
<div class="db-card-title">
|
||||
<span class="title-dot primary"></span>
|
||||
Siswa per Kelas
|
||||
</div>
|
||||
<span class="db-card-badge">Bar chart</span>
|
||||
</div>
|
||||
<div class="db-card-body">
|
||||
<div style="position: relative; height: 280px;">
|
||||
<canvas id="chartKelas" role="img" aria-label="Bar chart jumlah siswa per kelas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card shadow border-left-success">
|
||||
<div class="card-body">
|
||||
<h6>Jumlah Guru</h6>
|
||||
<h4>{{ $jumlahGuru }}</h4>
|
||||
{{-- CHART DISTRIBUSI NILAI --}}
|
||||
<div class="db-card">
|
||||
<div class="db-card-header">
|
||||
<div class="db-card-title">
|
||||
<span class="title-dot teal"></span>
|
||||
Distribusi Nilai
|
||||
</div>
|
||||
<span class="db-card-badge">Pie chart</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card shadow border-left-warning">
|
||||
<div class="card-body">
|
||||
<h6>Jumlah Kelas</h6>
|
||||
<h4>{{ $jumlahKelas }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card shadow border-left-danger">
|
||||
<div class="card-body">
|
||||
<h6>Mata Pelajaran</h6>
|
||||
<h4>{{ $jumlahMapel }}</h4>
|
||||
<div class="db-card-body">
|
||||
<div style="position: relative; height: 280px;">
|
||||
<canvas id="chartNilai" role="img" aria-label="Pie chart distribusi nilai siswa"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- SISWA TERBARU --}}
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<h5>Siswa Terbaru</h5>
|
||||
<ul>
|
||||
@foreach($siswaTerbaru as $siswa)
|
||||
<li>
|
||||
{{ $siswa->nama }}
|
||||
({{ $siswa->kelas->nama ?? '-' }})
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
{{-- DATA LISTS --}}
|
||||
<p class="section-label">Data Terkini</p>
|
||||
<div class="data-lists-grid">
|
||||
|
||||
{{-- SISWA TERBARU --}}
|
||||
<div class="db-card">
|
||||
<div class="db-card-header">
|
||||
<div class="db-card-title">
|
||||
<span class="title-dot primary"></span>
|
||||
Siswa Terbaru
|
||||
</div>
|
||||
</div>
|
||||
@forelse($siswaTerbaru as $i => $siswa)
|
||||
<div class="data-row">
|
||||
<div class="data-row-left">
|
||||
<span class="data-row-index">{{ str_pad($loop->iteration, 2, '0', STR_PAD_LEFT) }}</span>
|
||||
<span class="data-row-name">{{ $siswa->nama }}</span>
|
||||
</div>
|
||||
<span class="data-row-sub">{{ $siswa->kelas->nama ?? '—' }}</span>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-state">Belum ada data siswa</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
{{-- NILAI TERTINGGI --}}
|
||||
<div class="db-card">
|
||||
<div class="db-card-header">
|
||||
<div class="db-card-title">
|
||||
<span class="title-dot teal"></span>
|
||||
Nilai Tertinggi
|
||||
</div>
|
||||
</div>
|
||||
@forelse($nilaiTertinggi as $n)
|
||||
<div class="data-row">
|
||||
<div class="data-row-left">
|
||||
<span class="data-row-index">{{ str_pad($loop->iteration, 2, '0', STR_PAD_LEFT) }}</span>
|
||||
<span class="data-row-name">{{ $n->siswa->nama ?? '—' }}</span>
|
||||
</div>
|
||||
@php $score = $n->nilai; @endphp
|
||||
<span class="score-badge {{ $score >= 85 ? 'high' : ($score >= 70 ? 'mid' : 'low') }}">
|
||||
{{ $score }}
|
||||
</span>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-state">Belum ada data nilai</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- 🔥 NILAI TERTINGGI --}}
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<h5>Nilai Tertinggi</h5>
|
||||
<ul>
|
||||
@foreach($nilaiTertinggi as $n)
|
||||
<li>
|
||||
{{ $n->siswa->nama ?? '-' }}
|
||||
- {{ $n->nilai }}
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
{{-- QUICK ACTIONS --}}
|
||||
<div class="quick-actions">
|
||||
<div class="qa-left">
|
||||
<h5>Aksi Cepat</h5>
|
||||
<small>Tambah atau kelola data langsung dari sini</small>
|
||||
</div>
|
||||
<div class="qa-buttons">
|
||||
<a href="{{ route('admin.guru.create') }}" class="btn-qa primary">
|
||||
<i class="fa-solid fa-plus" style="font-size:11px;"></i> Tambah Guru
|
||||
</a>
|
||||
<a href="{{ route('admin.siswa.create') }}" class="btn-qa success">
|
||||
<i class="fa-solid fa-plus" style="font-size:11px;"></i> Tambah Siswa
|
||||
</a>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="btn-qa outline">
|
||||
<i class="fa-solid fa-book" style="font-size:11px;"></i> Mata Pelajaran
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
const kelasLabels = {!! json_encode($kelasLabels ?? []) !!};
|
||||
const kelasData = {!! json_encode($kelasData ?? []) !!};
|
||||
|
||||
const nilaiLabels = {!! json_encode(array_keys(($nilaiDistribusi ?? collect())->toArray())) !!};
|
||||
const nilaiData = {!! json_encode(array_values(($nilaiDistribusi ?? collect())->toArray())) !!};
|
||||
|
||||
console.log("Kelas:", kelasLabels, kelasData);
|
||||
console.log("Nilai:", nilaiLabels, nilaiData);
|
||||
|
||||
Chart.defaults.font.family = "'Plus Jakarta Sans', sans-serif";
|
||||
Chart.defaults.font.size = 12;
|
||||
|
||||
// ── CHART KELAS (Bar) ──────────────────────────────────
|
||||
if (kelasLabels.length > 0) {
|
||||
new Chart(document.getElementById('chartKelas'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: kelasLabels,
|
||||
datasets: [{
|
||||
label: 'Jumlah Siswa',
|
||||
data: kelasData,
|
||||
backgroundColor: 'rgba(26, 86, 219, 0.12)',
|
||||
borderColor: 'rgba(26, 86, 219, 0.9)',
|
||||
borderWidth: 2,
|
||||
borderRadius: 6,
|
||||
borderSkipped: false,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: '#111827',
|
||||
titleColor: '#fff',
|
||||
bodyColor: '#9ca3af',
|
||||
padding: 12,
|
||||
cornerRadius: 8,
|
||||
displayColors: false,
|
||||
callbacks: {
|
||||
label: ctx => ` ${ctx.parsed.y} siswa`
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
border: { display: false },
|
||||
ticks: { color: '#9ca3af', autoSkip: false, maxRotation: 30 }
|
||||
},
|
||||
y: {
|
||||
grid: { color: 'rgba(0,0,0,0.05)', drawTicks: false },
|
||||
border: { display: false, dash: [4, 4] },
|
||||
ticks: { color: '#9ca3af', padding: 8, precision: 0 },
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── CHART NILAI (Pie) ──────────────────────────────────
|
||||
if (nilaiLabels.length > 0) {
|
||||
new Chart(document.getElementById('chartNilai'), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: nilaiLabels,
|
||||
datasets: [{
|
||||
data: nilaiData,
|
||||
backgroundColor: [
|
||||
'rgba(26, 86, 219, 0.85)',
|
||||
'rgba(13, 148, 136, 0.85)',
|
||||
'rgba(217, 119, 6, 0.85)',
|
||||
'rgba(220, 38, 38, 0.85)',
|
||||
'rgba(99, 102, 241, 0.85)',
|
||||
'rgba(236, 72, 153, 0.85)',
|
||||
],
|
||||
borderColor: '#ffffff',
|
||||
borderWidth: 3,
|
||||
hoverOffset: 6,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
color: '#6b7280',
|
||||
padding: 14,
|
||||
boxWidth: 10,
|
||||
boxHeight: 10,
|
||||
font: { size: 12, weight: '500' }
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: '#111827',
|
||||
titleColor: '#fff',
|
||||
bodyColor: '#9ca3af',
|
||||
padding: 12,
|
||||
cornerRadius: 8,
|
||||
callbacks: {
|
||||
label: ctx => ` ${ctx.label}: ${ctx.parsed}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -1,96 +1,525 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h2 class="mb-4 text-primary">Tambah Guru</h2>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-light: #eff6ff;
|
||||
--primary-border: #bfdbfe;
|
||||
--primary-dark: #1d4ed8;
|
||||
--success: #16a34a;
|
||||
--success-light: #f0fdf4;
|
||||
--success-border: #bbf7d0;
|
||||
--success-dark: #15803d;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fef2f2;
|
||||
--danger-border: #fecaca;
|
||||
--info: #0284c7;
|
||||
--info-light: #f0f9ff;
|
||||
--info-border: #bae6fd;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-secondary: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--border-hover: #cbd5e1;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
||||
--shadow-md: 0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus: 0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-xl: 18px;
|
||||
}
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul class="mb-0">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<form action="{{ route('admin.guru.store') }}" method="POST">
|
||||
@csrf
|
||||
/* ── PAKSA AREA KONTEN MENGISI RUANG YANG ADA ── */
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 24px !important;
|
||||
background: #f1f5f9;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
.form-wrapper {
|
||||
animation: fadeInUp 0.4s ease both;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
{{-- NAMA --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Nama</label>
|
||||
<input type="text" name="nama" class="form-control" required>
|
||||
</div>
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
{{-- NIP --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">NIP</label>
|
||||
<input type="text" name="nip" class="form-control" required>
|
||||
</div>
|
||||
.back-nav {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-muted);
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
margin-bottom: 22px;
|
||||
transition: color 0.15s;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.back-nav:hover { color: var(--primary); text-decoration: none; }
|
||||
.back-nav svg { width: 16px; height: 16px; }
|
||||
|
||||
{{-- MAPEL --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Mata Pelajaran</label>
|
||||
<select name="mapel_id" class="form-control" required>
|
||||
<option value="">-- Pilih Mapel --</option>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}">{{ $m->nama_mapel }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
.form-page-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
{{-- TELEPON --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Telepon</label>
|
||||
<input type="text" name="telepon" class="form-control" required>
|
||||
</div>
|
||||
.form-page-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--primary-light);
|
||||
border: 1px solid var(--primary-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.form-page-icon svg { width: 24px; height: 24px; color: var(--primary); }
|
||||
|
||||
{{-- ALAMAT --}}
|
||||
<div class="col-md-12 mb-3">
|
||||
<label class="form-label fw-bold">Alamat</label>
|
||||
<input type="text" name="alamat" class="form-control" required>
|
||||
</div>
|
||||
.form-page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 4px 0;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
.form-page-subtitle { font-size: 13.5px; color: var(--text-secondary); }
|
||||
|
||||
</div>
|
||||
.steps-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
margin-bottom: 24px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 16px 22px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
.step-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
.step-item:not(:last-child)::after {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: var(--border);
|
||||
margin: 0 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.step-num {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-light);
|
||||
border: 1.5px solid var(--primary-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.step-label { font-size: 12.5px; font-weight: 600; color: var(--text-secondary); }
|
||||
.step-desc { font-size: 11.5px; color: var(--text-muted); margin-top: 1px; }
|
||||
|
||||
<hr>
|
||||
.error-alert {
|
||||
background: var(--danger-light);
|
||||
border: 1px solid var(--danger-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.error-alert-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
color: var(--danger);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.error-alert-header svg { width: 16px; height: 16px; }
|
||||
.error-list {
|
||||
margin: 0;
|
||||
padding: 0 0 0 20px;
|
||||
font-size: 13px;
|
||||
color: var(--danger);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
<h5 class="text-primary">Akun Login</h5>
|
||||
.form-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.form-card-header {
|
||||
padding: 17px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.section-dot { width: 8px; height: 8px; border-radius: 50%; }
|
||||
.dot-blue { background: var(--primary); }
|
||||
.dot-green { background: var(--success); }
|
||||
.form-card-header-label {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
}
|
||||
.form-card-body { padding: 26px 24px; }
|
||||
|
||||
{{-- EMAIL --}}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Email</label>
|
||||
<input type="email" name="email" class="form-control" required>
|
||||
</div>
|
||||
.form-row-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.form-row-full { margin-bottom: 18px; }
|
||||
|
||||
{{-- INFO PASSWORD --}}
|
||||
<div class="alert alert-info">
|
||||
Password default: <b>12345678</b><br>
|
||||
Guru wajib mengganti password saat login pertama.
|
||||
</div>
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
.form-label .required { color: var(--danger); margin-left: 3px; }
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('admin.guru.index') }}" class="btn btn-secondary">
|
||||
Kembali
|
||||
</a>
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-primary);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
outline: none;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
appearance: none;
|
||||
}
|
||||
.form-control:focus { border-color: var(--primary); box-shadow: var(--shadow-focus); }
|
||||
.form-control:hover:not(:focus) { border-color: var(--border-hover); }
|
||||
.form-control::placeholder { color: var(--text-muted); }
|
||||
.form-control.is-invalid {
|
||||
border-color: var(--danger);
|
||||
box-shadow: 0 0 0 3px rgba(220,38,38,0.1);
|
||||
}
|
||||
|
||||
<button type="submit" class="btn btn-success" onclick="this.disabled=true; this.form.submit();">
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
select.form-control {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 38px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</form>
|
||||
.info-box {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
background: var(--info-light);
|
||||
border: 1px solid var(--info-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 18px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.info-box-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: #e0f2fe;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.info-box-icon svg { width: 16px; height: 16px; color: var(--info); }
|
||||
.info-box-title { font-size: 13px; font-weight: 600; color: #075985; margin-bottom: 4px; }
|
||||
.info-box-desc { font-size: 12.5px; color: #0369a1; line-height: 1.6; }
|
||||
.info-box-desc b { font-weight: 700; }
|
||||
|
||||
.form-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--surface);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.btn-cancel:hover { background: var(--surface-secondary); color: var(--text-primary); border-color: var(--border-hover); text-decoration: none; }
|
||||
.btn-cancel svg { width: 15px; height: 15px; }
|
||||
|
||||
.btn-submit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 26px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--success);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
box-shadow: 0 2px 8px rgba(22,163,74,0.3);
|
||||
}
|
||||
.btn-submit svg { width: 15px; height: 15px; }
|
||||
.btn-submit:hover { background: var(--success-dark); box-shadow: 0 4px 14px rgba(22,163,74,0.4); transform: translateY(-1px); }
|
||||
.btn-submit:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.form-row-grid { grid-template-columns: 1fr; }
|
||||
.steps-indicator { flex-direction: column; align-items: flex-start; gap: 12px; }
|
||||
.step-item::after { display: none; }
|
||||
.form-card-body { padding: 20px 16px; }
|
||||
.form-footer { padding: 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="form-wrapper">
|
||||
|
||||
{{-- BACK LINK --}}
|
||||
<a href="{{ route('admin.guru.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
Kembali ke Daftar Guru
|
||||
</a>
|
||||
|
||||
{{-- PAGE HEADER --}}
|
||||
<div class="form-page-header">
|
||||
<div class="form-page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="form-page-title">Tambah Guru Baru</h2>
|
||||
<p class="form-page-subtitle">Daftarkan guru baru beserta akun login ke sistem</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STEPS INDICATOR --}}
|
||||
<div class="steps-indicator">
|
||||
<div class="step-item">
|
||||
<div class="step-num">1</div>
|
||||
<div>
|
||||
<div class="step-label">Data Pribadi</div>
|
||||
<div class="step-desc">Nama, NIP, alamat</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-num">2</div>
|
||||
<div>
|
||||
<div class="step-label">Mata Pelajaran</div>
|
||||
<div class="step-desc">Pilih bidang studi</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-num">3</div>
|
||||
<div>
|
||||
<div class="step-label">Akun Login</div>
|
||||
<div class="step-desc">Email & password</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- VALIDATION ERRORS --}}
|
||||
@if ($errors->any())
|
||||
<div class="error-alert">
|
||||
<div class="error-alert-header">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856C18.07 19.126 19 17.643 19 16V8a2 2 0 00-2-2H7a2 2 0 00-2 2v8c0 1.643.93 3.126 2.062 4z"/>
|
||||
</svg>
|
||||
Terdapat {{ $errors->count() }} kesalahan pada form
|
||||
</div>
|
||||
<ul class="error-list">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.guru.store') }}" method="POST" id="formTambahGuru">
|
||||
@csrf
|
||||
|
||||
{{-- CARD 1: DATA PRIBADI --}}
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="section-dot dot-blue"></div>
|
||||
<span class="form-card-header-label">Data Pribadi Guru</span>
|
||||
</div>
|
||||
<div class="form-card-body">
|
||||
|
||||
<div class="form-row-grid">
|
||||
<div>
|
||||
<label class="form-label">Nama Lengkap <span class="required">*</span></label>
|
||||
<input type="text" name="nama" value="{{ old('nama') }}"
|
||||
class="form-control {{ $errors->has('nama') ? 'is-invalid' : '' }}"
|
||||
placeholder="Masukkan nama lengkap guru" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">NIP <span class="required">*</span></label>
|
||||
<input type="text" name="nip" value="{{ old('nip') }}"
|
||||
class="form-control {{ $errors->has('nip') ? 'is-invalid' : '' }}"
|
||||
placeholder="Nomor Induk Pegawai" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row-grid">
|
||||
<div>
|
||||
<label class="form-label">Mata Pelajaran <span class="required">*</span></label>
|
||||
<select name="mapel_id" class="form-control {{ $errors->has('mapel_id') ? 'is-invalid' : '' }}" required>
|
||||
<option value="">— Pilih Mata Pelajaran —</option>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}" {{ old('mapel_id') == $m->id ? 'selected' : '' }}>
|
||||
{{ $m->nama_mapel }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Nomor Telepon <span class="required">*</span></label>
|
||||
<input type="text" name="telepon" value="{{ old('telepon') }}"
|
||||
class="form-control {{ $errors->has('telepon') ? 'is-invalid' : '' }}"
|
||||
placeholder="Contoh: 081234567890" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row-full">
|
||||
<label class="form-label">Alamat Lengkap <span class="required">*</span></label>
|
||||
<input type="text" name="alamat" value="{{ old('alamat') }}"
|
||||
class="form-control {{ $errors->has('alamat') ? 'is-invalid' : '' }}"
|
||||
placeholder="Jl. ..., Kelurahan, Kecamatan, Kota" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- CARD 2: AKUN LOGIN --}}
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="section-dot dot-green"></div>
|
||||
<span class="form-card-header-label">Akun Login Guru</span>
|
||||
</div>
|
||||
<div class="form-card-body">
|
||||
|
||||
<div class="form-row-full">
|
||||
<label class="form-label">Alamat Email <span class="required">*</span></label>
|
||||
<input type="email" name="email" value="{{ old('email') }}"
|
||||
class="form-control {{ $errors->has('email') ? 'is-invalid' : '' }}"
|
||||
placeholder="contoh@sekolah.sch.id" required>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<div class="info-box-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="info-box-title">Informasi Password</div>
|
||||
<div class="info-box-desc">
|
||||
Password default yang ditetapkan adalah <b>12345678</b>.<br>
|
||||
Guru wajib mengganti password saat pertama kali login ke sistem.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<a href="{{ route('admin.guru.index') }}" class="btn-cancel">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" class="btn-submit" id="btnSubmit">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Simpan Data Guru
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('btnSubmit').addEventListener('click', function(e) {
|
||||
this.disabled = true;
|
||||
this.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="animation:spin 0.8s linear infinite">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
Menyimpan...
|
||||
`;
|
||||
document.getElementById('formTambahGuru').submit();
|
||||
});
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = '@keyframes spin { to { transform: rotate(360deg); } }';
|
||||
document.head.appendChild(style);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,46 +1,453 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h2 class="mb-4 text-warning">Edit Guru</h2>
|
||||
|
||||
<div class="card shadow-lg border-0 rounded-3">
|
||||
<div class="card-body">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-light: #eff6ff;
|
||||
--primary-border: #bfdbfe;
|
||||
--primary-dark: #1d4ed8;
|
||||
--warning: #d97706;
|
||||
--warning-light: #fffbeb;
|
||||
--warning-border: #fde68a;
|
||||
--warning-dark: #b45309;
|
||||
--success: #16a34a;
|
||||
--danger: #dc2626;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-secondary: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--border-hover: #cbd5e1;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
||||
--shadow-md: 0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus: 0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-xl: 18px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 24px !important;
|
||||
background: #f1f5f9;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.form-wrapper {
|
||||
animation: fadeInUp 0.4s ease both;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* BREADCRUMB */
|
||||
.back-nav {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-muted);
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
margin-bottom: 22px;
|
||||
transition: color 0.15s;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.back-nav:hover { color: var(--primary); text-decoration: none; }
|
||||
.back-nav svg { width: 16px; height: 16px; }
|
||||
|
||||
/* PAGE HEADING */
|
||||
.form-page-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.form-page-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--warning-light);
|
||||
border: 1px solid var(--warning-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.form-page-icon svg { width: 24px; height: 24px; color: var(--warning); }
|
||||
|
||||
.form-page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 4px 0;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.form-page-subtitle {
|
||||
font-size: 13.5px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* CARD */
|
||||
.form-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-card-header {
|
||||
padding: 18px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-card-header-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
}
|
||||
|
||||
.section-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--warning);
|
||||
}
|
||||
|
||||
.form-card-body { padding: 28px 24px; }
|
||||
|
||||
/* SECTION DIVIDER */
|
||||
.form-section-label {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* FORM ELEMENTS */
|
||||
.form-row-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.form-row-full { margin-bottom: 18px; }
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.form-label .required {
|
||||
color: var(--danger);
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-primary);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
outline: none;
|
||||
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: var(--shadow-focus);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:hover:not(:focus) {
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
.form-control::placeholder { color: var(--text-muted); }
|
||||
|
||||
select.form-control {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 38px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* GURU INFO BANNER */
|
||||
.guru-info-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
background: var(--warning-light);
|
||||
border: 1px solid var(--warning-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.guru-avatar {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--warning-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--warning-dark);
|
||||
flex-shrink: 0;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.guru-info-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.guru-info-meta {
|
||||
font-size: 12.5px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* FOOTER ACTIONS */
|
||||
.form-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--surface);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.btn-cancel svg { width: 15px; height: 15px; }
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: var(--surface-secondary);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-update {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 26px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--warning);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
box-shadow: 0 2px 8px rgba(217,119,6,0.3);
|
||||
}
|
||||
|
||||
.btn-update svg { width: 15px; height: 15px; }
|
||||
|
||||
.btn-update:hover {
|
||||
background: var(--warning-dark);
|
||||
box-shadow: 0 4px 14px rgba(217,119,6,0.4);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-update:active { transform: scale(0.98); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.form-row-grid { grid-template-columns: 1fr; }
|
||||
.form-card-body { padding: 20px 16px; }
|
||||
.form-footer { padding: 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4 py-2">
|
||||
<div class="form-wrapper">
|
||||
|
||||
{{-- BACK LINK --}}
|
||||
<a href="{{ route('admin.guru.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
Kembali ke Daftar Guru
|
||||
</a>
|
||||
|
||||
{{-- PAGE HEADER --}}
|
||||
<div class="form-page-header">
|
||||
<div class="form-page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="form-page-title">Edit Data Guru</h2>
|
||||
<p class="form-page-subtitle">Perbarui informasi data guru yang sudah terdaftar</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- CURRENT GURU BANNER --}}
|
||||
<div class="guru-info-banner">
|
||||
<div class="guru-avatar">
|
||||
{{ strtoupper(substr($guru->nama, 0, 2)) }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="guru-info-name">{{ $guru->nama }}</div>
|
||||
<div class="guru-info-meta">NIP: {{ $guru->nip ?: '—' }} • {{ $guru->mapel->nama_mapel ?? 'Belum ada mapel' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FORM CARD --}}
|
||||
<div class="form-card">
|
||||
|
||||
<div class="form-card-header">
|
||||
<div class="section-dot"></div>
|
||||
<span class="form-card-header-label">Informasi Data Guru</span>
|
||||
</div>
|
||||
|
||||
<div class="form-card-body">
|
||||
<form action="{{ route('admin.guru.update', $guru->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Nama</label>
|
||||
<input type="text" name="nama" value="{{ $guru->nama }}" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">NIP</label>
|
||||
<input type="text" name="nip" value="{{ $guru->nip }}" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Mapel</label>
|
||||
<input type="text" name="mapel" value="{{ $guru->mapel }}" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Alamat</label>
|
||||
<input type="text" name="alamat" value="{{ $guru->alamat }}" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Telepon</label>
|
||||
<input type="text" name="telepon" value="{{ $guru->telepon }}" class="form-control">
|
||||
{{-- ROW 1 --}}
|
||||
<div class="form-row-grid">
|
||||
<div>
|
||||
<label class="form-label">Nama Lengkap <span class="required">*</span></label>
|
||||
<input type="text" name="nama" value="{{ old('nama', $guru->nama) }}"
|
||||
class="form-control" placeholder="Masukkan nama lengkap" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">NIP</label>
|
||||
<input type="text" name="nip" value="{{ old('nip', $guru->nip) }}"
|
||||
class="form-control" placeholder="Nomor Induk Pegawai">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('admin.guru.index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Kembali
|
||||
</a>
|
||||
<button type="submit" class="btn btn-warning">
|
||||
<i class="fas fa-edit"></i> Update
|
||||
</button>
|
||||
{{-- ROW 2 --}}
|
||||
<div class="form-row-grid">
|
||||
<div>
|
||||
<label class="form-label">Mata Pelajaran <span class="required">*</span></label>
|
||||
<select name="mapel_id" class="form-control" required>
|
||||
<option value="">— Pilih Mata Pelajaran —</option>
|
||||
@forelse($mapel as $m)
|
||||
<option value="{{ $m->id }}" {{ $guru->mapel_id == $m->id ? 'selected' : '' }}>
|
||||
{{ $m->nama_mapel }}
|
||||
</option>
|
||||
@empty
|
||||
<option disabled>Tidak ada data mapel</option>
|
||||
@endforelse
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Nomor Telepon</label>
|
||||
<input type="text" name="telepon" value="{{ old('telepon', $guru->telepon) }}"
|
||||
class="form-control" placeholder="Contoh: 081234567890">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ROW 3 --}}
|
||||
<div class="form-row-full">
|
||||
<label class="form-label">Alamat</label>
|
||||
<input type="text" name="alamat" value="{{ old('alamat', $guru->alamat) }}"
|
||||
class="form-control" placeholder="Masukkan alamat lengkap">
|
||||
</div>
|
||||
|
||||
{{-- Tombol di dalam form, sebelum </form> --}}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<a href="{{ route('admin.guru.index') }}" class="btn-cancel">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" form="formEditGuru" class="btn-update" id="btnUpdate">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
// Attach form id so footer button can submit
|
||||
const form = document.querySelector('form[action*="update"]');
|
||||
if (form) form.id = 'formEditGuru';
|
||||
})();
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -3,281 +3,680 @@
|
|||
@section('content')
|
||||
|
||||
<style>
|
||||
/* ===== GLOBAL ===== */
|
||||
* {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-light: #eff6ff;
|
||||
--primary-border: #bfdbfe;
|
||||
--success: #16a34a;
|
||||
--success-light: #f0fdf4;
|
||||
--success-border: #bbf7d0;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fef2f2;
|
||||
--danger-border: #fecaca;
|
||||
--warning: #d97706;
|
||||
--warning-light: #fffbeb;
|
||||
--warning-border: #fde68a;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-secondary: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--border-hover: #cbd5e1;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
||||
--shadow-md: 0 4px 16px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus: 0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-xl: 18px;
|
||||
}
|
||||
|
||||
/* ===== PAGE HEADER ===== */
|
||||
.page-title {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.3px;
|
||||
color: #1f2937;
|
||||
margin: 0;
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.guru-wrapper {
|
||||
padding: 0 4px;
|
||||
animation: fadeInUp 0.4s ease both;
|
||||
}
|
||||
|
||||
/* ===== CARD ===== */
|
||||
.card {
|
||||
border: none !important;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
|
||||
overflow: hidden;
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ===== TABLE ===== */
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
/* ---- HEADER ---- */
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 28px;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.table thead {
|
||||
background: linear-gradient(to right, #f1f3f6, #e7ebf0);
|
||||
}
|
||||
.page-header-left {}
|
||||
|
||||
.table thead th {
|
||||
border: none !important;
|
||||
padding: 15px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #5b6472;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
background: #ffffff;
|
||||
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:nth-child(even) {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: #f3f6fa;
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
border-top: 1px solid #edf1f5 !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
border-bottom: none !important;
|
||||
padding: 15px 14px;
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* ===== BADGE ===== */
|
||||
.badge {
|
||||
.page-breadcrumb {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.page-breadcrumb span { color: var(--primary); font-weight: 500; }
|
||||
|
||||
.page-title {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
letter-spacing: -0.4px;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 13.5px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.btn-add-guru {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 7px 12px;
|
||||
border-radius: 999px;
|
||||
letter-spacing: 0.2px;
|
||||
padding: 11px 22px;
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
transition: background 0.2s, box-shadow 0.2s, transform 0.15s;
|
||||
box-shadow: 0 2px 8px rgba(37,99,235,0.25);
|
||||
}
|
||||
|
||||
.badge-mapel {
|
||||
background: #eef4ff;
|
||||
color: #2f5ea8;
|
||||
border: 1px solid #dbe7ff;
|
||||
.btn-add-guru:hover {
|
||||
background: #1d4ed8;
|
||||
box-shadow: 0 4px 14px rgba(37,99,235,0.35);
|
||||
transform: translateY(-1px);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.badge-status-active {
|
||||
background: #eaf8ef;
|
||||
color: #1f9d57;
|
||||
border: 1px solid #d5f0df;
|
||||
.btn-add-guru svg { width: 16px; height: 16px; }
|
||||
|
||||
/* ---- STATS ROW ---- */
|
||||
.stats-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: 14px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.badge-status-inactive {
|
||||
background: #fdeeee;
|
||||
color: #d14343;
|
||||
border: 1px solid #f8d6d6;
|
||||
.stat-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 18px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
/* ===== BUTTON ===== */
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
.stat-card:hover { box-shadow: var(--shadow-md); }
|
||||
|
||||
.stat-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stat-icon.blue { background: var(--primary-light); }
|
||||
.stat-icon.green { background: var(--success-light); }
|
||||
.stat-icon.red { background: var(--danger-light); }
|
||||
|
||||
.stat-icon svg { width: 20px; height: 20px; }
|
||||
.stat-icon.blue svg { color: var(--primary); }
|
||||
.stat-icon.green svg { color: var(--success); }
|
||||
.stat-icon.red svg { color: var(--danger); }
|
||||
|
||||
.stat-info {}
|
||||
.stat-label { font-size: 12px; color: var(--text-muted); font-weight: 500; }
|
||||
.stat-value { font-size: 22px; font-weight: 700; color: var(--text-primary); line-height: 1.1; margin-top: 2px; }
|
||||
|
||||
/* ---- ALERT ---- */
|
||||
.alert-success-custom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background: var(--success-light);
|
||||
border: 1px solid var(--success-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
color: var(--success);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 6px 12px;
|
||||
.alert-success-custom svg { width: 18px; height: 18px; flex-shrink: 0; }
|
||||
.alert-close { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--success); padding: 0; opacity: 0.7; }
|
||||
.alert-close:hover { opacity: 1; }
|
||||
|
||||
/* ---- TABLE CARD ---- */
|
||||
.table-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 18px 22px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-wrap svg {
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
color: var(--text-muted);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 8px 12px 8px 34px;
|
||||
font-size: 13px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-secondary);
|
||||
color: var(--text-primary);
|
||||
width: 210px;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: var(--shadow-focus);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.search-input::placeholder { color: var(--text-muted); }
|
||||
|
||||
/* ---- TABLE ---- */
|
||||
.table-responsive { overflow-x: auto; }
|
||||
|
||||
table.guru-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 860px;
|
||||
}
|
||||
|
||||
.guru-table thead tr {
|
||||
background: var(--surface-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.guru-table thead th {
|
||||
padding: 13px 16px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.7px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.guru-table thead th:last-child { text-align: center; }
|
||||
|
||||
.guru-table tbody tr {
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.guru-table tbody tr:last-child { border-bottom: none; }
|
||||
.guru-table tbody tr:hover { background: #f8faff; }
|
||||
|
||||
.guru-table tbody td {
|
||||
padding: 14px 16px;
|
||||
font-size: 13.5px;
|
||||
color: var(--text-primary);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.cell-no {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.cell-nama {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.cell-nip {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Plus Jakarta Sans', monospace;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.cell-muted {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.badge-mapel {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
border: 1px solid var(--primary-border);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 4px 11px;
|
||||
}
|
||||
|
||||
.badge-aktif {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: var(--success-light);
|
||||
color: var(--success);
|
||||
border: 1px solid var(--success-border);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 4px 11px;
|
||||
}
|
||||
|
||||
.badge-aktif::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.badge-nonaktif {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: var(--danger-light);
|
||||
color: var(--danger);
|
||||
border: 1px solid var(--danger-border);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 4px 11px;
|
||||
}
|
||||
|
||||
.badge-nonaktif::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--danger);
|
||||
}
|
||||
|
||||
/* ---- ACTION BUTTONS ---- */
|
||||
.action-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
min-width: 92px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 6px 13px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
border-radius: var(--radius-sm);
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-soft {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #dbe2ea;
|
||||
color: #374151;
|
||||
.btn-action svg { width: 13px; height: 13px; }
|
||||
|
||||
.btn-edit {
|
||||
background: var(--surface-secondary);
|
||||
color: var(--text-secondary);
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.btn-soft:hover {
|
||||
background: #eef2f6;
|
||||
border-color: #cfd8e3;
|
||||
color: #111827;
|
||||
.btn-edit:hover {
|
||||
background: #f1f5f9;
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-outline-warning:hover,
|
||||
.btn-outline-success:hover {
|
||||
color: #fff;
|
||||
.btn-delete {
|
||||
background: var(--danger-light);
|
||||
color: var(--danger);
|
||||
border-color: var(--danger-border);
|
||||
}
|
||||
|
||||
/* ===== ALERT ===== */
|
||||
.alert {
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.04);
|
||||
.btn-delete:hover {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ===== EMPTY STATE ===== */
|
||||
.empty-row td {
|
||||
padding: 28px 14px !important;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
.btn-nonaktif {
|
||||
background: var(--warning-light);
|
||||
color: var(--warning);
|
||||
border-color: var(--warning-border);
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ===== */
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
.btn-nonaktif:hover {
|
||||
background: #fef3c7;
|
||||
color: #b45309;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.table thead th,
|
||||
.table tbody td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-aktifkan {
|
||||
background: var(--success-light);
|
||||
color: var(--success);
|
||||
border-color: var(--success-border);
|
||||
}
|
||||
|
||||
.btn-aktifkan:hover {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ---- EMPTY STATE ---- */
|
||||
.empty-state {
|
||||
padding: 64px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: var(--surface-secondary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
}
|
||||
|
||||
.empty-icon svg { width: 28px; height: 28px; color: var(--text-muted); }
|
||||
|
||||
.empty-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 13.5px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ---- TABLE FOOTER ---- */
|
||||
.table-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 22px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="container-fluid px-4 py-2 guru-wrapper">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="page-title">Daftar Guru</h3>
|
||||
<a href="{{ route('admin.guru.create') }}" class="btn btn-primary px-4">
|
||||
{{-- HEADER --}}
|
||||
<div class="page-header">
|
||||
<div class="page-header-left">
|
||||
<div class="page-breadcrumb">Manajemen › <span>Guru</span></div>
|
||||
<h3 class="page-title">Data Guru</h3>
|
||||
<p class="page-subtitle">Kelola seluruh data guru aktif dan nonaktif</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.guru.create') }}" class="btn-add-guru">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
Tambah Guru
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
{{ session('success') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama</th>
|
||||
<th>NIP</th>
|
||||
<th>Mapel</th>
|
||||
<th>Email</th>
|
||||
<th>Alamat</th>
|
||||
<th>Telepon</th>
|
||||
<th>Status</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($guru as $g)
|
||||
<tr>
|
||||
<td class="text-muted">{{ $loop->iteration }}</td>
|
||||
|
||||
<td class="fw-semibold text-start">
|
||||
{{ $g->nama }}
|
||||
</td>
|
||||
|
||||
<td>{{ $g->nip }}</td>
|
||||
|
||||
<td>
|
||||
<span class="badge badge-mapel">
|
||||
{{ $g->mapel->nama_mapel ?? '-' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-muted">
|
||||
{{ $g->user->email ?? '-' }}
|
||||
</td>
|
||||
|
||||
<td class="text-start text-muted">
|
||||
{{ $g->alamat }}
|
||||
</td>
|
||||
|
||||
<td>{{ $g->telepon }}</td>
|
||||
|
||||
<td>
|
||||
@if($g->status == 'aktif')
|
||||
<span class="badge badge-status-active">
|
||||
Aktif
|
||||
</span>
|
||||
@else
|
||||
<span class="badge badge-status-inactive">
|
||||
Nonaktif
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<div class="d-flex justify-content-center gap-2 flex-wrap">
|
||||
|
||||
<a href="{{ route('admin.guru.edit', $g->id) }}"
|
||||
class="btn btn-sm btn-soft btn-action">
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<form action="{{ route('admin.guru.destroy', $g->id) }}"
|
||||
method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit"
|
||||
class="btn btn-sm btn-soft btn-action text-danger"
|
||||
onclick="return confirm('Yakin hapus data?')">
|
||||
Hapus
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@if($g->status == 'aktif')
|
||||
<a href="{{ route('admin.guru.nonaktif', $g->id) }}"
|
||||
class="btn btn-sm btn-outline-warning btn-action">
|
||||
Nonaktifkan
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('admin.guru.aktifkan', $g->id) }}"
|
||||
class="btn btn-sm btn-outline-success btn-action">
|
||||
Aktifkan
|
||||
</a>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@empty
|
||||
<tr class="empty-row">
|
||||
<td colspan="9" class="text-center">
|
||||
Belum ada data guru
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
{{-- STATS --}}
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon blue">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a4 4 0 00-5-3.87M9 20H4v-2a4 4 0 015-3.87m4 5.87a4 4 0 110-8 4 4 0 010 8zm6-12a4 4 0 10-8 0 4 4 0 008 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">Total Guru</div>
|
||||
<div class="stat-value">{{ $guru->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon green">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">Guru Aktif</div>
|
||||
<div class="stat-value">{{ $guru->where('status','aktif')->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon red">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-label">Nonaktif</div>
|
||||
<div class="stat-value">{{ $guru->where('status','nonaktif')->count() }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div class="alert-success-custom" id="alertSuccess">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>{{ session('success') }}</span>
|
||||
<button class="alert-close" onclick="document.getElementById('alertSuccess').remove()">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- TABLE CARD --}}
|
||||
<div class="table-card">
|
||||
<div class="table-toolbar">
|
||||
<span class="toolbar-label">Daftar Guru <span style="font-weight:400;color:var(--text-muted);font-size:13px;">({{ $guru->count() }} data)</span></span>
|
||||
<div class="toolbar-right">
|
||||
<div class="search-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M17 11A6 6 0 115 11a6 6 0 0112 0z"/>
|
||||
</svg>
|
||||
<input type="text" class="search-input" placeholder="Cari nama, NIP..." id="searchInput" oninput="filterTable()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="guru-table" id="guruTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama Guru</th>
|
||||
<th>NIP</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Email</th>
|
||||
<th>Telepon</th>
|
||||
<th>Status</th>
|
||||
<th style="text-align:center;">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="guruTableBody">
|
||||
@forelse($guru as $g)
|
||||
<tr>
|
||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
||||
<td>
|
||||
<div class="cell-nama">{{ $g->nama }}</div>
|
||||
@if($g->alamat)
|
||||
<div class="cell-muted" style="font-size:12px;margin-top:2px;">{{ Str::limit($g->alamat, 40) }}</div>
|
||||
@endif
|
||||
</td>
|
||||
<td class="cell-nip">{{ $g->nip ?: '—' }}</td>
|
||||
<td>
|
||||
<span class="badge-mapel">{{ $g->mapel->nama_mapel ?? '—' }}</span>
|
||||
</td>
|
||||
<td class="cell-muted">{{ $g->user->email ?? '—' }}</td>
|
||||
<td class="cell-muted">{{ $g->telepon ?: '—' }}</td>
|
||||
<td>
|
||||
@if($g->status == 'aktif')
|
||||
<span class="badge-aktif">Aktif</span>
|
||||
@else
|
||||
<span class="badge-nonaktif">Nonaktif</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<div class="action-group">
|
||||
<a href="{{ route('admin.guru.edit', $g->id) }}" class="btn-action btn-edit">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/>
|
||||
</svg>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<form action="{{ route('admin.guru.destroy', $g->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Yakin ingin menghapus data guru ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn-action btn-delete">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M9 7h6m2 0a1 1 0 00-1-1h-4a1 1 0 00-1 1m6 0H7"/>
|
||||
</svg>
|
||||
Hapus
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@if($g->status == 'aktif')
|
||||
<a href="{{ route('admin.guru.nonaktif', $g->id) }}" class="btn-action btn-nonaktif">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636"/>
|
||||
</svg>
|
||||
Nonaktifkan
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('admin.guru.aktifkan', $g->id) }}" class="btn-action btn-aktifkan">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Aktifkan
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a4 4 0 00-5-3.87M9 20H4v-2a4 4 0 015-3.87m4 5.87a4 4 0 110-8 4 4 0 010 8zm6-12a4 4 0 10-8 0 4 4 0 008 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum ada data guru</div>
|
||||
<div class="empty-desc">Mulai tambahkan data guru menggunakan tombol di atas.</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-footer">
|
||||
<span id="footerCount">Menampilkan {{ $guru->count() }} data</span>
|
||||
<span>Sistem Manajemen Guru • {{ now()->format('d M Y') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function filterTable() {
|
||||
const q = document.getElementById('searchInput').value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#guruTableBody tr');
|
||||
let visible = 0;
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
const show = text.includes(q);
|
||||
row.style.display = show ? '' : 'none';
|
||||
if (show) visible++;
|
||||
});
|
||||
document.getElementById('footerCount').textContent = 'Menampilkan ' + visible + ' data';
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,81 +1,173 @@
|
|||
{{-- ================================================================
|
||||
FILE 2: resources/views/admin/jadwal/create.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Tambah Jadwal</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
:root {
|
||||
--brand:#1a56db; --brand-light:#e8f0fe; --brand-dark:#1648c0;
|
||||
--danger:#dc2626; --page-bg:#f4f6fa; --surface:#ffffff;
|
||||
--border:rgba(0,0,0,0.08); --text-1:#111827; --text-2:#6b7280; --text-3:#9ca3af;
|
||||
--r-sm:8px; --r-md:12px; --r-lg:16px;
|
||||
--sh-sm:0 1px 4px rgba(0,0,0,.06),0 2px 8px rgba(0,0,0,.04);
|
||||
}
|
||||
*,*::before,*::after{font-family:'Plus Jakarta Sans',sans-serif;box-sizing:border-box;}
|
||||
.form-wrap{background:var(--page-bg);min-height:100vh;padding:28px 32px 56px;}
|
||||
.form-nav{display:flex;align-items:center;gap:8px;margin-bottom:20px;}
|
||||
.form-nav a{font-size:13px;font-weight:500;color:var(--text-2);text-decoration:none;display:inline-flex;align-items:center;gap:5px;transition:color .12s;}
|
||||
.form-nav a:hover{color:var(--brand);}
|
||||
.form-nav-sep{color:var(--text-3);font-size:13px;}
|
||||
.form-nav-current{font-size:13px;font-weight:600;color:var(--text-1);}
|
||||
.form-page-title{margin-bottom:24px;}
|
||||
.form-page-title .eyebrow{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--brand);margin-bottom:4px;}
|
||||
.form-page-title h1{font-size:24px;font-weight:700;color:var(--text-1);margin:0;letter-spacing:-.4px;}
|
||||
.form-card{
|
||||
background:var(--surface);
|
||||
border-radius:var(--r-lg);
|
||||
border:1px solid var(--border);
|
||||
box-shadow:var(--sh-sm);
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
}
|
||||
.form-card-header{padding:20px 24px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;}
|
||||
.form-card-icon{width:38px;height:38px;border-radius:9px;background:var(--brand-light);color:var(--brand);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;}
|
||||
.form-card-header-text h5{font-size:15px;font-weight:700;color:var(--text-1);margin:0 0 2px;}
|
||||
.form-card-header-text p{font-size:12px;color:var(--text-2);margin:0;}
|
||||
.form-card-body{padding:24px;}
|
||||
.form-errors{background:#fff5f5;border:1px solid rgba(220,38,38,.2);border-left:4px solid var(--danger);border-radius:var(--r-md);padding:14px 16px;margin-bottom:22px;}
|
||||
.form-errors ul{margin:0;padding-left:18px;}
|
||||
.form-errors li{font-size:13px;color:var(--danger);font-weight:500;line-height:1.7;}
|
||||
.field{margin-bottom:18px;}
|
||||
.field-label{font-size:13px;font-weight:600;color:var(--text-1);margin-bottom:7px;display:block;}
|
||||
.field-input,.field-select{width:100%;padding:10px 13px;border:1.5px solid var(--border);border-radius:var(--r-sm);font-size:14px;color:var(--text-1);background:#fff;outline:none;transition:border-color .15s,box-shadow .15s;font-family:'Plus Jakarta Sans',sans-serif;appearance:none;}
|
||||
.field-input::placeholder{color:var(--text-3);}
|
||||
.field-input:focus,.field-select:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(26,86,219,.1);}
|
||||
.field-input[type="time"]{font-family:'DM Mono',monospace;cursor:pointer;}
|
||||
.select-wrap{position:relative;}
|
||||
.select-wrap::after{content:'\f107';font-family:'Font Awesome 6 Free';font-weight:900;position:absolute;right:13px;top:50%;transform:translateY(-50%);color:var(--text-3);pointer-events:none;font-size:12px;}
|
||||
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
|
||||
.form-footer{display:flex;align-items:center;gap:10px;padding-top:20px;border-top:1px solid var(--border);margin-top:24px;}
|
||||
.btn-submit{display:inline-flex;align-items:center;gap:7px;background:var(--brand);color:#fff;border:none;border-radius:var(--r-sm);padding:10px 22px;font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;transition:all .15s ease;box-shadow:0 2px 8px rgba(26,86,219,.25);font-family:'Plus Jakarta Sans',sans-serif;}
|
||||
.btn-submit:hover{background:var(--brand-dark);transform:translateY(-1px);color:#fff;}
|
||||
.btn-submit i{font-size:11px;}
|
||||
.btn-back{display:inline-flex;align-items:center;gap:7px;background:#f8fafc;color:var(--text-1);border:1.5px solid rgba(0,0,0,.12);border-radius:var(--r-sm);padding:10px 18px;font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;transition:all .12s ease;font-family:'Plus Jakarta Sans',sans-serif;}
|
||||
.btn-back:hover{background:#eef2f6;color:var(--text-1);}
|
||||
@media(max-width:768px){.form-wrap{padding:20px 16px 40px;}.form-card{max-width:100%;}.field-row{grid-template-columns:1fr;}}
|
||||
</style>
|
||||
|
||||
<div class="form-wrap">
|
||||
|
||||
<div class="form-nav">
|
||||
<a href="{{ route('admin.jadwal.index') }}"><i class="fas fa-arrow-left" style="font-size:11px;"></i> Jadwal</a>
|
||||
<span class="form-nav-sep">/</span>
|
||||
<span class="form-nav-current">Tambah Baru</span>
|
||||
</div>
|
||||
|
||||
<div class="form-page-title">
|
||||
<div class="eyebrow">Akademik</div>
|
||||
<h1>Tambah Jadwal</h1>
|
||||
</div>
|
||||
|
||||
@if(session('error'))
|
||||
<div class="form-errors" style="max-width:600px;">
|
||||
<ul><li>{{ session('error') }}</li></ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="form-card-icon"><i class="fas fa-calendar-plus"></i></div>
|
||||
<div class="form-card-header-text">
|
||||
<h5>Data Jadwal Baru</h5>
|
||||
<p>Isi semua informasi jadwal pelajaran</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-card-body">
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="form-errors">
|
||||
<ul>@foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.jadwal.store') }}" method="POST">
|
||||
@csrf
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label>Kelas</label>
|
||||
<select name="kelas_id" class="form-control">
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
{{-- KELAS --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Kelas</label>
|
||||
<div class="select-wrap">
|
||||
<select name="kelas_id" class="field-select">
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ old('kelas_id') == $k->id ? 'selected' : '' }}>{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Mata Pelajaran</label>
|
||||
<select name="mata_pelajaran_id" class="form-control">
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}">{{ $m->nama_mapel }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
{{-- MAPEL --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Mata Pelajaran</label>
|
||||
<div class="select-wrap">
|
||||
<select name="mata_pelajaran_id" class="field-select">
|
||||
<option value="">-- Pilih Mata Pelajaran --</option>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}" {{ old('mata_pelajaran_id') == $m->id ? 'selected' : '' }}>{{ $m->nama_mapel }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 🔥 TAMBAHAN GURU -->
|
||||
<div class="form-group">
|
||||
<label>Guru</label>
|
||||
<select name="guru_id" class="form-control">
|
||||
@foreach($guru as $g)
|
||||
<option value="{{ $g->id }}">{{ $g->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
{{-- GURU --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Guru</label>
|
||||
<div class="select-wrap">
|
||||
<select name="guru_id" class="field-select">
|
||||
<option value="">-- Pilih Guru --</option>
|
||||
@foreach($guru as $g)
|
||||
<option value="{{ $g->id }}" {{ old('guru_id') == $g->id ? 'selected' : '' }}>{{ $g->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Hari</label>
|
||||
<select name="hari" class="form-control">
|
||||
<option>Senin</option>
|
||||
<option>Selasa</option>
|
||||
<option>Rabu</option>
|
||||
<option>Kamis</option>
|
||||
<option>Jumat</option>
|
||||
</select>
|
||||
</div>
|
||||
{{-- HARI --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Hari</label>
|
||||
<div class="select-wrap">
|
||||
<select name="hari" class="field-select">
|
||||
<option value="">-- Pilih Hari --</option>
|
||||
@foreach(['Senin','Selasa','Rabu','Kamis','Jumat'] as $h)
|
||||
<option value="{{ $h }}" {{ old('hari') == $h ? 'selected' : '' }}>{{ $h }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Jam Mulai</label>
|
||||
<input type="time" name="jam_mulai" class="form-control">
|
||||
</div>
|
||||
{{-- JAM --}}
|
||||
<div class="field-row">
|
||||
<div class="field">
|
||||
<label class="field-label">Jam Mulai</label>
|
||||
<input type="time" name="jam_mulai" value="{{ old('jam_mulai') }}" class="field-input">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="field-label">Jam Selesai</label>
|
||||
<input type="time" name="jam_selesai" value="{{ old('jam_selesai') }}" class="field-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Jam Selesai</label>
|
||||
<input type="time" name="jam_selesai" class="form-control">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">
|
||||
<i class="fas fa-save"></i> Simpan
|
||||
</button>
|
||||
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn btn-secondary">
|
||||
Kembali
|
||||
</a>
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn-submit"><i class="fas fa-save"></i> Simpan</button>
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn-back"><i class="fas fa-times"></i> Batal</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,201 @@
|
|||
{{-- ================================================================
|
||||
FILE 3: resources/views/admin/jadwal/edit.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Edit Jadwal</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
:root {
|
||||
--brand:#1a56db; --brand-light:#e8f0fe; --brand-dark:#1648c0;
|
||||
--warning:#d97706; --warning-light:#fef3c7;
|
||||
--page-bg:#f4f6fa; --surface:#ffffff;
|
||||
--border:rgba(0,0,0,0.08); --text-1:#111827; --text-2:#6b7280; --text-3:#9ca3af;
|
||||
--r-sm:8px; --r-md:12px; --r-lg:16px;
|
||||
--sh-sm:0 1px 4px rgba(0,0,0,.06),0 2px 8px rgba(0,0,0,.04);
|
||||
}
|
||||
*,*::before,*::after{font-family:'Plus Jakarta Sans',sans-serif;box-sizing:border-box;}
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
.container,
|
||||
.container-fluid{
|
||||
width:100% !important;
|
||||
max-width:100% !important;
|
||||
padding-left:0 !important;
|
||||
padding-right:0 !important;
|
||||
}
|
||||
.form-wrap{
|
||||
background:var(--page-bg);
|
||||
min-height:100vh;
|
||||
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
|
||||
padding:28px 32px 56px;
|
||||
}
|
||||
.form-nav{display:flex;align-items:center;gap:8px;margin-bottom:20px;}
|
||||
.form-nav a{font-size:13px;font-weight:500;color:var(--text-2);text-decoration:none;display:inline-flex;align-items:center;gap:5px;transition:color .12s;}
|
||||
.form-nav a:hover{color:var(--brand);}
|
||||
.form-nav-sep{color:var(--text-3);font-size:13px;}
|
||||
.form-nav-current{font-size:13px;font-weight:600;color:var(--text-1);}
|
||||
.form-page-title{margin-bottom:24px;}
|
||||
.form-page-title .eyebrow{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--warning);margin-bottom:4px;}
|
||||
.form-page-title h1{font-size:24px;font-weight:700;color:var(--text-1);margin:0;letter-spacing:-.4px;}
|
||||
.edit-banner{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
background:var(--warning-light);
|
||||
border:1px solid rgba(217,119,6,.2);
|
||||
border-left:4px solid var(--warning);
|
||||
border-radius:var(--r-md);
|
||||
padding:13px 16px;
|
||||
margin-bottom:22px;
|
||||
font-size:13px;
|
||||
font-weight:500;
|
||||
color:#92400e;
|
||||
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
}
|
||||
.form-card{
|
||||
background:var(--surface);
|
||||
border-radius:var(--r-lg);
|
||||
border:1px solid var(--border);
|
||||
box-shadow:var(--sh-sm);
|
||||
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
}
|
||||
.form-card-header{padding:20px 24px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;}
|
||||
.form-card-icon{width:38px;height:38px;border-radius:9px;background:var(--warning-light);color:var(--warning);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;}
|
||||
.form-card-header-text h5{font-size:15px;font-weight:700;color:var(--text-1);margin:0 0 2px;}
|
||||
.form-card-header-text p{font-size:12px;color:var(--text-2);margin:0;}
|
||||
.form-card-body{padding:24px;}
|
||||
.field{margin-bottom:18px;}
|
||||
.field-label{font-size:13px;font-weight:600;color:var(--text-1);margin-bottom:7px;display:block;}
|
||||
.field-input,.field-select{width:100%;padding:10px 13px;border:1.5px solid var(--border);border-radius:var(--r-sm);font-size:14px;color:var(--text-1);background:#fff;outline:none;transition:border-color .15s,box-shadow .15s;font-family:'Plus Jakarta Sans',sans-serif;appearance:none;}
|
||||
.field-input:focus,.field-select:focus{border-color:var(--brand);box-shadow:0 0 0 3px rgba(26,86,219,.1);}
|
||||
.field-input[type="time"]{font-family:'DM Mono',monospace;cursor:pointer;}
|
||||
.select-wrap{position:relative;}
|
||||
.select-wrap::after{content:'\f107';font-family:'Font Awesome 6 Free';font-weight:900;position:absolute;right:13px;top:50%;transform:translateY(-50%);color:var(--text-3);pointer-events:none;font-size:12px;}
|
||||
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
|
||||
.form-footer{display:flex;align-items:center;gap:10px;padding-top:20px;border-top:1px solid var(--border);margin-top:24px;}
|
||||
.btn-submit{display:inline-flex;align-items:center;gap:7px;background:var(--warning);color:#fff;border:none;border-radius:var(--r-sm);padding:10px 22px;font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;transition:all .15s ease;box-shadow:0 2px 8px rgba(217,119,6,.3);font-family:'Plus Jakarta Sans',sans-serif;}
|
||||
.btn-submit:hover{background:#b45309;transform:translateY(-1px);color:#fff;}
|
||||
.btn-submit i{font-size:11px;}
|
||||
.btn-back{display:inline-flex;align-items:center;gap:7px;background:#f8fafc;color:var(--text-1);border:1.5px solid rgba(0,0,0,.12);border-radius:var(--r-sm);padding:10px 18px;font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;transition:all .12s ease;font-family:'Plus Jakarta Sans',sans-serif;}
|
||||
.btn-back:hover{background:#eef2f6;color:var(--text-1);}
|
||||
@media(max-width:768px){.form-wrap{padding:20px 16px 40px;}.form-card,.edit-banner{max-width:100%;}.field-row{grid-template-columns:1fr;}}
|
||||
</style>
|
||||
|
||||
<div class="form-wrap">
|
||||
|
||||
<div class="form-nav">
|
||||
<a href="{{ route('admin.jadwal.index') }}"><i class="fas fa-arrow-left" style="font-size:11px;"></i> Jadwal</a>
|
||||
<span class="form-nav-sep">/</span>
|
||||
<span class="form-nav-current">Edit</span>
|
||||
</div>
|
||||
|
||||
<div class="form-page-title">
|
||||
<div class="eyebrow">Akademik</div>
|
||||
<h1>Edit Jadwal</h1>
|
||||
</div>
|
||||
|
||||
<div class="edit-banner">
|
||||
<i class="fas fa-pen" style="font-size:14px;flex-shrink:0;"></i>
|
||||
Mengedit jadwal:
|
||||
<strong> {{ $jadwal->mapel->nama_mapel ?? '' }} — {{ $jadwal->hari }}</strong>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="form-card-icon"><i class="fas fa-calendar-alt"></i></div>
|
||||
<div class="form-card-header-text">
|
||||
<h5>Ubah Data Jadwal</h5>
|
||||
<p>Perubahan akan langsung tersimpan ke sistem</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-card-body">
|
||||
|
||||
<form action="{{ route('admin.jadwal.update', $jadwal->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<!-- KELAS -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Kelas</label>
|
||||
<select name="kelas_id" class="form-control" required>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ $jadwal->kelas_id == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
{{-- KELAS --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Kelas</label>
|
||||
<div class="select-wrap">
|
||||
<select name="kelas_id" class="field-select" required>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ $jadwal->kelas_id == $k->id ? 'selected' : '' }}>{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MAPEL -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Mata Pelajaran</label>
|
||||
<select name="mata_pelajaran_id" class="form-control" required>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}"
|
||||
{{ $jadwal->mata_pelajaran_id == $m->id ? 'selected' : '' }}>
|
||||
{{ $m->nama_mapel }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
{{-- MAPEL --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Mata Pelajaran</label>
|
||||
<div class="select-wrap">
|
||||
<select name="mata_pelajaran_id" class="field-select" required>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}" {{ $jadwal->mata_pelajaran_id == $m->id ? 'selected' : '' }}>{{ $m->nama_mapel }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 🔥 TAMBAHAN WAJIB: GURU -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Guru</label>
|
||||
<select name="guru_id" class="form-control" required>
|
||||
@foreach($guru as $g)
|
||||
<option value="{{ $g->id }}"
|
||||
{{ $jadwal->guru_id == $g->id ? 'selected' : '' }}>
|
||||
{{ $g->nama }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
{{-- GURU --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Guru</label>
|
||||
<div class="select-wrap">
|
||||
<select name="guru_id" class="field-select" required>
|
||||
@foreach($guru as $g)
|
||||
<option value="{{ $g->id }}" {{ $jadwal->guru_id == $g->id ? 'selected' : '' }}>{{ $g->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HARI -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Hari</label>
|
||||
<input type="text" name="hari" value="{{ $jadwal->hari }}" class="form-control" required>
|
||||
{{-- HARI --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Hari</label>
|
||||
<div class="select-wrap">
|
||||
<select name="hari" class="field-select" required>
|
||||
@foreach(['Senin','Selasa','Rabu','Kamis','Jumat'] as $h)
|
||||
<option value="{{ $h }}" {{ $jadwal->hari == $h ? 'selected' : '' }}>{{ $h }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAM MULAI -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Jam Mulai</label>
|
||||
<input type="time" name="jam_mulai" value="{{ $jadwal->jam_mulai }}" class="form-control" required>
|
||||
{{-- JAM --}}
|
||||
<div class="field-row">
|
||||
<div class="field">
|
||||
<label class="field-label">Jam Mulai</label>
|
||||
<input type="time" name="jam_mulai" value="{{ $jadwal->jam_mulai }}" class="field-input" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="field-label">Jam Selesai</label>
|
||||
<input type="time" name="jam_selesai" value="{{ $jadwal->jam_selesai }}" class="field-input" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAM SELESAI -->
|
||||
<div class="form-group mb-3">
|
||||
<label>Jam Selesai</label>
|
||||
<input type="time" name="jam_selesai" value="{{ $jadwal->jam_selesai }}" class="form-control" required>
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn-submit"><i class="fas fa-save"></i> Update</button>
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn-back"><i class="fas fa-arrow-left"></i> Kembali</a>
|
||||
</div>
|
||||
|
||||
<!-- BUTTON -->
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Update
|
||||
</button>
|
||||
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn btn-secondary">
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
@endsection
|
||||
|
||||
|
||||
{{-- ================================================================
|
||||
FILE 4: resources/views/admin/jadwal/grid.blade.php
|
||||
================================================================ --}}
|
||||
|
|
@ -1,188 +1,328 @@
|
|||
{{-- ================================================================
|
||||
FILE: resources/views/admin/jadwal/index.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Judul -->
|
||||
<h1 class="h3 mb-2 text-gray-800">Data Jadwal </h1>
|
||||
<p class="mb-4">Pengelolaan jadwal SMA Negeri 1 Bondowoso</p>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
|
||||
<!-- Alert -->
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
{{ session('success') }}
|
||||
:root {
|
||||
--brand: #1a56db;
|
||||
--brand-light: #e8f0fe;
|
||||
--brand-dark: #1648c0;
|
||||
--success: #0d9488;
|
||||
--success-light:#ccfbf1;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fee2e2;
|
||||
--warning: #d97706;
|
||||
--warning-light:#fef3c7;
|
||||
--page-bg: #f4f6fa;
|
||||
--surface: #ffffff;
|
||||
--border: rgba(0,0,0,0.07);
|
||||
--border-md: rgba(0,0,0,0.12);
|
||||
--text-1: #111827;
|
||||
--text-2: #6b7280;
|
||||
--text-3: #9ca3af;
|
||||
--r-sm: 8px; --r-md: 12px; --r-lg: 16px;
|
||||
--sh-sm: 0 1px 4px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
||||
--sh-md: 0 4px 16px rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
.jd-wrap { background: var(--page-bg); min-height: 100vh; padding: 28px 32px 56px; }
|
||||
|
||||
/* header */
|
||||
.jd-header { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 24px; }
|
||||
.jd-header-left .eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--brand); margin-bottom: 4px; }
|
||||
.jd-header-left h1 { font-size: 24px; font-weight: 700; color: var(--text-1); margin: 0; letter-spacing: -.4px; }
|
||||
.jd-header-left p { font-size: 13px; color: var(--text-2); margin: 4px 0 0; }
|
||||
|
||||
.btn-add {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: var(--brand); color: #fff; border: none;
|
||||
border-radius: var(--r-sm); padding: 10px 20px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .15s ease;
|
||||
box-shadow: 0 2px 8px rgba(26,86,219,.25); white-space: nowrap;
|
||||
}
|
||||
.btn-add:hover { background: var(--brand-dark); transform: translateY(-1px); color: #fff; box-shadow: 0 4px 12px rgba(26,86,219,.35); }
|
||||
.btn-add i { font-size: 11px; }
|
||||
|
||||
/* alert */
|
||||
.jd-alert {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
background: var(--success-light); border: 1px solid rgba(13,148,136,.2);
|
||||
border-left: 4px solid var(--success); border-radius: var(--r-md);
|
||||
padding: 13px 16px; margin-bottom: 20px;
|
||||
font-size: 13px; font-weight: 500; color: #065f46; position: relative;
|
||||
}
|
||||
.jd-alert-close { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; color: #065f46; font-size: 18px; opacity: .6; }
|
||||
.jd-alert-close:hover { opacity: 1; }
|
||||
|
||||
/* filter card */
|
||||
.filter-card {
|
||||
background: var(--surface); border-radius: var(--r-lg);
|
||||
border: 1px solid var(--border); box-shadow: var(--sh-sm);
|
||||
padding: 16px 20px; margin-bottom: 16px;
|
||||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||||
}
|
||||
.filter-label { font-size: 12px; font-weight: 700; color: var(--text-3); text-transform: uppercase; letter-spacing: .08em; white-space: nowrap; }
|
||||
.filter-select {
|
||||
flex: 1; min-width: 200px; max-width: 300px;
|
||||
padding: 9px 13px; border: 1.5px solid var(--border);
|
||||
border-radius: var(--r-sm); font-size: 13px; color: var(--text-1);
|
||||
background: #f8fafc; outline: none; cursor: pointer;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.filter-select:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(26,86,219,.1); background: #fff; }
|
||||
.btn-filter {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 9px 16px; border-radius: var(--r-sm);
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
border: none; text-decoration: none; transition: all .13s ease;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.btn-filter.primary { background: var(--brand); color: #fff; }
|
||||
.btn-filter.primary:hover { background: var(--brand-dark); color: #fff; }
|
||||
.btn-filter.reset { background: #f8fafc; color: var(--text-2); border: 1.5px solid var(--border-md); }
|
||||
.btn-filter.reset:hover { background: #eef2f6; color: var(--text-1); }
|
||||
.btn-filter i { font-size: 11px; }
|
||||
|
||||
/* info banner */
|
||||
.filter-info {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
background: var(--brand-light); border: 1px solid rgba(26,86,219,.15);
|
||||
border-radius: var(--r-sm); padding: 8px 14px;
|
||||
font-size: 13px; font-weight: 500; color: var(--brand);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* table card */
|
||||
.jd-card { background: var(--surface); border-radius: var(--r-lg); border: 1px solid var(--border); box-shadow: var(--sh-sm); overflow: hidden; }
|
||||
.jd-card-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 15px 20px; border-bottom: 1px solid var(--border); }
|
||||
.jd-count { font-size: 13px; font-weight: 600; color: var(--text-2); }
|
||||
.jd-count strong { color: var(--text-1); font-weight: 700; }
|
||||
|
||||
/* table */
|
||||
.jd-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
||||
.jd-table thead tr { background: #f8fafc; border-bottom: 1px solid var(--border); }
|
||||
.jd-table thead th { padding: 12px 16px; font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); white-space: nowrap; border: none; }
|
||||
.jd-table thead th:first-child { padding-left: 20px; }
|
||||
.jd-table thead th:last-child { padding-right: 20px; text-align: right; }
|
||||
.jd-table tbody tr { border-bottom: 1px solid var(--border); transition: background .12s; }
|
||||
.jd-table tbody tr:last-child { border-bottom: none; }
|
||||
.jd-table tbody tr:hover { background: #f8fafc; }
|
||||
.jd-table td { padding: 13px 16px; color: var(--text-1); vertical-align: middle; border: none; }
|
||||
.jd-table td:first-child { padding-left: 20px; }
|
||||
.jd-table td:last-child { padding-right: 20px; }
|
||||
|
||||
.cell-no { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-3); font-weight: 500; width: 44px; }
|
||||
.cell-mapel { font-weight: 600; color: var(--text-1); }
|
||||
.cell-muted { color: var(--text-2); font-size: 13px; }
|
||||
|
||||
/* hari pills */
|
||||
.hari-pill {
|
||||
display: inline-flex; align-items: center; padding: 3px 10px;
|
||||
border-radius: 999px; font-size: 12px; font-weight: 600;
|
||||
}
|
||||
.hari-senin { background: #eff6ff; color: #1d4ed8; }
|
||||
.hari-selasa { background: #f0fdf4; color: #15803d; }
|
||||
.hari-rabu { background: #fefce8; color: #a16207; }
|
||||
.hari-kamis { background: #fdf4ff; color: #9333ea; }
|
||||
.hari-jumat { background: #fff7ed; color: #c2410c; }
|
||||
.hari-default { background: #f1f5f9; color: #475569; }
|
||||
|
||||
/* jam badge */
|
||||
.jam-badge {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
font-family: 'DM Mono', monospace; font-size: 12.5px;
|
||||
font-weight: 500; color: var(--text-2);
|
||||
background: #f8fafc; border: 1px solid var(--border);
|
||||
border-radius: 6px; padding: 3px 9px;
|
||||
}
|
||||
.jam-badge i { font-size: 10px; color: var(--text-3); }
|
||||
|
||||
/* kelas pill */
|
||||
.kelas-pill { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--brand-light); color: var(--brand); border: 1px solid rgba(26,86,219,.15); }
|
||||
|
||||
/* guru pill */
|
||||
.guru-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; color: var(--text-1); }
|
||||
.guru-avatar { width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg, #1a56db, #6366f1); color: #fff; font-size: 9px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
|
||||
/* action buttons */
|
||||
.action-group { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
|
||||
.btn-act { display: inline-flex; align-items: center; gap: 5px; padding: 6px 12px; border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer; text-decoration: none; border: 1px solid; transition: all .12s ease; white-space: nowrap; font-family: 'Plus Jakarta Sans', sans-serif; background: none; }
|
||||
.btn-act i { font-size: 10px; }
|
||||
.btn-edit { background: #f8fafc; border-color: var(--border-md); color: var(--text-1); }
|
||||
.btn-edit:hover { background: #eef2f6; color: var(--text-1); }
|
||||
.btn-del { background: #fff5f5; border-color: rgba(220,38,38,.18); color: var(--danger); }
|
||||
.btn-del:hover { background: var(--danger-light); border-color: rgba(220,38,38,.35); color: var(--danger); }
|
||||
|
||||
/* empty */
|
||||
.empty-state { padding: 52px 24px; text-align: center; }
|
||||
.empty-icon { width: 54px; height: 54px; border-radius: 50%; background: #f1f5f9; display: flex; align-items: center; justify-content: center; margin: 0 auto 14px; color: var(--text-3); font-size: 22px; }
|
||||
.empty-state p { font-size: 14px; color: var(--text-2); font-weight: 500; margin: 0 0 4px; }
|
||||
.empty-state small { font-size: 12px; color: var(--text-3); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.jd-wrap { padding: 20px 16px 40px; }
|
||||
.jd-header { flex-direction: column; align-items: flex-start; gap: 14px; }
|
||||
.jd-scroll { overflow-x: auto; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="jd-wrap">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="jd-header">
|
||||
<div class="jd-header-left">
|
||||
<div class="eyebrow">Akademik</div>
|
||||
<h1>Data Jadwal</h1>
|
||||
<p>Pengelolaan jadwal SMA Negeri 1 Bondowoso</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.jadwal.create') }}" class="btn-add">
|
||||
<i class="fas fa-plus"></i> Tambah Jadwal
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div class="jd-alert" id="jdAlert">
|
||||
<i class="fas fa-check-circle" style="font-size:15px;flex-shrink:0;"></i>
|
||||
{{ session('success') }}
|
||||
<button class="jd-alert-close" onclick="document.getElementById('jdAlert').remove()">×</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Card -->
|
||||
<div class="card shadow mb-4">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Tabel Jadwal</h6>
|
||||
|
||||
<a href="{{ route('admin.jadwal.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-plus"></i> Tambah Jadwal
|
||||
{{-- FILTER --}}
|
||||
<form method="GET" action="{{ route('admin.jadwal.index') }}">
|
||||
<div class="filter-card">
|
||||
<span class="filter-label"><i class="fas fa-filter" style="font-size:10px;"></i> Filter Kelas</span>
|
||||
<select name="kelas_id" class="filter-select">
|
||||
<option value="">Semua Kelas</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="btn-filter primary">
|
||||
<i class="fas fa-search"></i> Tampilkan
|
||||
</button>
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn-filter reset">
|
||||
<i class="fas fa-times"></i> Reset
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- FILTER -->
|
||||
<div class="card-body pb-0">
|
||||
<form method="GET" action="{{ route('admin.jadwal.index') }}">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4 mb-2">
|
||||
<select name="kelas_id" class="form-control">
|
||||
<option value="">-- Semua Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 mb-2">
|
||||
<button class="btn btn-primary btn-block">
|
||||
<i class="fas fa-filter"></i> Filter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 mb-2">
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn btn-secondary btn-block">
|
||||
Reset
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(request('kelas_id') && $kelas->where('id', request('kelas_id'))->first())
|
||||
<div class="alert alert-info mt-2">
|
||||
Menampilkan jadwal kelas:
|
||||
<b>{{ $kelas->where('id', request('kelas_id'))->first()->nama_kelas }}</b>
|
||||
</div>
|
||||
@endif
|
||||
@if(request('kelas_id') && $kelas->where('id', request('kelas_id'))->first())
|
||||
<div class="filter-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
Menampilkan jadwal kelas:
|
||||
<strong>{{ $kelas->where('id', request('kelas_id'))->first()->nama_kelas }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Body -->
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
|
||||
<!-- <form method="GET" class="mb-3 d-flex gap-2">
|
||||
<select name="kelas_id" class="form-control w-25">
|
||||
<option value="">-- Semua Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<!-- <button class="btn btn-primary">
|
||||
<i class="fas fa-filter"></i> Filter
|
||||
</button>
|
||||
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="btn btn-secondary">
|
||||
Reset
|
||||
</a> -->
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered table-hover">
|
||||
|
||||
<thead class="thead-light text-center">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Guru</th>
|
||||
<th>Hari</th>
|
||||
<th>Jam</th>
|
||||
<th width="150px">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($jadwal as $j)
|
||||
<tr>
|
||||
<td class="text-center">{{ $loop->iteration }}</td>
|
||||
|
||||
<td class="text-center">
|
||||
<span class="badge badge-info px-3 py-2">
|
||||
{{ $j->kelas->nama_kelas ?? '-' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<b>{{ $j->mapel->nama_mapel ?? '-' }}</b>
|
||||
</td>
|
||||
|
||||
<!-- 🔥 TAMBAHAN GURU -->
|
||||
<td>
|
||||
<span class="badge badge-primary px-3 py-2">
|
||||
{{ $j->guru->nama ?? '-' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<span class="badge badge-secondary">
|
||||
{{ $j->hari }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<span class="badge badge-light">
|
||||
{{ \Carbon\Carbon::parse($j->jam_mulai)->format('H:i') }}
|
||||
-
|
||||
{{ \Carbon\Carbon::parse($j->jam_selesai)->format('H:i') }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
@if($jadwal->isEmpty())
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted">
|
||||
Tidak ada jadwal
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
<!-- EDIT -->
|
||||
<a href="{{ route('admin.jadwal.edit', $j->id) }}"
|
||||
class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
|
||||
<!-- DELETE -->
|
||||
<form action="{{ route('admin.jadwal.destroy', $j->id) }}"
|
||||
method="POST"
|
||||
class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"
|
||||
onclick="return confirm('Yakin hapus jadwal ini?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted">
|
||||
Data jadwal belum tersedia
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
{{-- TABLE CARD --}}
|
||||
<div class="jd-card">
|
||||
<div class="jd-card-toolbar">
|
||||
<div class="jd-count">
|
||||
Total <strong>{{ $jadwal->count() }}</strong> jadwal
|
||||
</div>
|
||||
</div>
|
||||
<div class="jd-scroll">
|
||||
<table class="jd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Guru</th>
|
||||
<th>Hari</th>
|
||||
<th>Jam</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($jadwal as $j)
|
||||
<tr>
|
||||
<td class="cell-no">{{ str_pad($loop->iteration, 2, '0', STR_PAD_LEFT) }}</td>
|
||||
|
||||
<td>
|
||||
<span class="kelas-pill">{{ $j->kelas->nama_kelas ?? '—' }}</span>
|
||||
</td>
|
||||
|
||||
<td class="cell-mapel">{{ $j->mapel->nama_mapel ?? '—' }}</td>
|
||||
|
||||
<td>
|
||||
<div class="guru-pill">
|
||||
<div class="guru-avatar">{{ strtoupper(substr($j->guru->nama ?? 'G', 0, 2)) }}</div>
|
||||
{{ $j->guru->nama ?? '—' }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@php
|
||||
$hariClass = match(strtolower($j->hari)) {
|
||||
'senin' => 'hari-senin',
|
||||
'selasa' => 'hari-selasa',
|
||||
'rabu' => 'hari-rabu',
|
||||
'kamis' => 'hari-kamis',
|
||||
'jumat' => 'hari-jumat',
|
||||
default => 'hari-default',
|
||||
};
|
||||
@endphp
|
||||
<span class="hari-pill {{ $hariClass }}">{{ $j->hari }}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span class="jam-badge">
|
||||
<i class="fas fa-clock"></i>
|
||||
{{ \Carbon\Carbon::parse($j->jam_mulai)->format('H:i') }} – {{ \Carbon\Carbon::parse($j->jam_selesai)->format('H:i') }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="action-group">
|
||||
<a href="{{ route('admin.jadwal.edit', $j->id) }}" class="btn-act btn-edit">
|
||||
<i class="fas fa-pen"></i> Edit
|
||||
</a>
|
||||
<form action="{{ route('admin.jadwal.destroy', $j->id) }}" method="POST" style="display:inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn-act btn-del"
|
||||
onclick="return confirm('Yakin hapus jadwal ini?')">
|
||||
<i class="fas fa-trash"></i> Hapus
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon"><i class="fas fa-calendar-times"></i></div>
|
||||
<p>Belum ada data jadwal</p>
|
||||
<small>Klik "Tambah Jadwal" untuk menambahkan.</small>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,40 +1,174 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Tambah Kelas</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
.sw-topbar { margin-bottom: 24px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 700; color: var(--text-dark); margin: 0 0 4px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #ECFDF5 0%, #D1FAE5 100%);
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 38px; height: 38px;
|
||||
background: var(--success);
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.sw-card-header h5 { font-size: 15px; font-weight: 700; color: #065F46; margin: 0; }
|
||||
.sw-card-header p { font-size: 12.5px; color: #047857; margin: 2px 0 0; }
|
||||
|
||||
.sw-card-body { padding: 28px 24px; }
|
||||
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 12px; font-weight: 700; color: var(--success);
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #A7F3D0, transparent);
|
||||
}
|
||||
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input, .sw-select {
|
||||
padding: 10px 14px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark);
|
||||
background: var(--neutral-light);
|
||||
outline: none;
|
||||
width: 100%;
|
||||
transition: border-color .15s, box-shadow .15s, background .15s;
|
||||
}
|
||||
.sw-input:focus, .sw-select:focus {
|
||||
border-color: var(--success);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(5,150,105,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
.sw-footer {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
border: none; cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
}
|
||||
.sw-btn-secondary { background: var(--surface); color: var(--text-mid); border: 1.5px solid var(--border); }
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-mid); }
|
||||
.sw-btn-success { background: var(--success); color: #fff; }
|
||||
.sw-btn-success:hover { background: #047857; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(5,150,105,.3); color: #fff; }
|
||||
|
||||
@media (max-width: 640px) { .sw-page { padding: 16px; } }
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
<div class="sw-topbar">
|
||||
<h3>Tambah Kelas</h3>
|
||||
<p>Daftarkan kelas baru ke dalam sistem</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Formulir Kelas Baru</h5>
|
||||
<p>Semua kolom bertanda <span style="color:#DC2626">*</span> wajib diisi</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Informasi Kelas</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.kelas.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nama Kelas</label>
|
||||
<input type="text" name="nama_kelas" class="form-control" placeholder="Contoh: X IPA 1">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Nama Kelas <span>*</span></label>
|
||||
<input type="text" name="nama_kelas" class="sw-input" placeholder="Contoh: X IPA 1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Jurusan</label>
|
||||
<select name="jurusan" class="form-control">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Jurusan <span>*</span></label>
|
||||
<select name="jurusan" class="sw-select">
|
||||
<option value="">-- Pilih Jurusan --</option>
|
||||
<option value="IPA">IPA</option>
|
||||
<option value="IPS">IPS</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">
|
||||
<i class="fas fa-save"></i> Simpan
|
||||
</button>
|
||||
|
||||
<a href="{{ route('admin.kelas.index') }}" class="btn btn-secondary">
|
||||
Kembali
|
||||
</a>
|
||||
<div class="sw-footer">
|
||||
<a href="{{ route('admin.kelas.index') }}" class="sw-btn sw-btn-secondary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali
|
||||
</a>
|
||||
<button type="submit" class="sw-btn sw-btn-success">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Simpan Kelas
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +1,218 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h2 class="mb-4 text-warning">Edit Kelas</h2>
|
||||
|
||||
<div class="card shadow-lg border-0">
|
||||
<div class="card-body">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
.sw-topbar { margin-bottom: 24px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 700; color: var(--text-dark); margin: 0 0 4px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #FFFBEB 0%, #FEF3C7 100%);
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 38px; height: 38px;
|
||||
background: var(--warning);
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.sw-card-header h5 { font-size: 15px; font-weight: 700; color: #92400E; margin: 0; }
|
||||
.sw-card-header p { font-size: 12.5px; color: #A16207; margin: 2px 0 0; }
|
||||
|
||||
.sw-id-chip {
|
||||
margin-left: auto;
|
||||
background: var(--warning-light);
|
||||
border: 1px solid #FDE68A;
|
||||
border-radius: 99px;
|
||||
padding: 5px 14px;
|
||||
font-size: 12px; font-weight: 600;
|
||||
color: var(--warning);
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sw-card-body { padding: 28px 24px; }
|
||||
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 12px; font-weight: 700; color: var(--warning);
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #FDE68A, transparent);
|
||||
}
|
||||
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input {
|
||||
padding: 10px 14px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark);
|
||||
background: var(--neutral-light);
|
||||
outline: none; width: 100%;
|
||||
transition: border-color .15s, box-shadow .15s, background .15s;
|
||||
}
|
||||
.sw-input:focus {
|
||||
border-color: var(--warning);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(217,119,6,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
.sw-alert-danger {
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
padding: 14px 16px;
|
||||
background: var(--danger-light);
|
||||
border: 1px solid #FECACA;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--danger);
|
||||
font-size: 13px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sw-alert-danger > div { display: flex; flex-direction: column; gap: 3px; }
|
||||
|
||||
.sw-footer {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
border: none; cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
}
|
||||
.sw-btn-secondary { background: var(--surface); color: var(--text-mid); border: 1.5px solid var(--border); }
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-mid); }
|
||||
.sw-btn-warning { background: var(--warning); color: #fff; }
|
||||
.sw-btn-warning:hover { background: #B45309; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(217,119,6,.3); color: #fff; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-id-chip { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
<div class="sw-topbar">
|
||||
<h3>Edit Kelas</h3>
|
||||
<p>Perbarui informasi kelas yang tersimpan di sistem</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Edit Kelas</h5>
|
||||
<p>Semua kolom bertanda <span style="color:#DC2626">*</span> wajib diisi</p>
|
||||
</div>
|
||||
<div class="sw-id-chip">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M8 12h8M8 16h5"/></svg>
|
||||
{{ $kelas->nama_kelas }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<div class="sw-alert-danger">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0;margin-top:2px"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
<div>
|
||||
@foreach ($errors->all() as $error)
|
||||
<div>{{ $error }}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Informasi Kelas</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.kelas.update', $kelas->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Nama Kelas</label>
|
||||
<input type="text" name="nama_kelas" class="form-control"
|
||||
value="{{ old('nama_kelas', $kelas->nama_kelas) }}" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Nama Kelas <span>*</span></label>
|
||||
<input type="text" name="nama_kelas" class="sw-input"
|
||||
value="{{ old('nama_kelas', $kelas->nama_kelas) }}"
|
||||
placeholder="Contoh: X IPA 1" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Jurusan</label>
|
||||
<input type="text" name="jurusan" class="form-control"
|
||||
value="{{ old('jurusan', $kelas->jurusan) }}" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Jurusan <span>*</span></label>
|
||||
<input type="text" name="jurusan" class="sw-input"
|
||||
value="{{ old('jurusan', $kelas->jurusan) }}"
|
||||
placeholder="Contoh: IPA" required>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('admin.kelas.index') }}" class="btn btn-secondary">
|
||||
<div class="sw-footer">
|
||||
<a href="{{ route('admin.kelas.index') }}" class="sw-btn sw-btn-secondary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn btn-warning">
|
||||
<i class="fas fa-edit"></i> Update
|
||||
<button type="submit" class="sw-btn sw-btn-warning">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Update Kelas
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,92 +1,157 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Judul -->
|
||||
<h1 class="h3 mb-2 text-gray-800">Data Kelas</h1>
|
||||
<p class="mb-4">Pengelolaan data kelas SMA Negeri 1 Bondowoso</p>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<!-- Alert -->
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
.sw-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; }
|
||||
.sw-topbar-left h3 { font-size: 22px; font-weight: 700; color: var(--text-dark); margin: 0; letter-spacing: -.3px; }
|
||||
.sw-topbar-left p { font-size: 13px; color: var(--text-soft); margin: 2px 0 0; }
|
||||
|
||||
.sw-btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: var(--radius-md); font-size: 13.5px; font-weight: 600; border: none; cursor: pointer; text-decoration: none; transition: all .18s ease; line-height: 1; }
|
||||
.sw-btn-primary { background: var(--primary); color: #fff; }
|
||||
.sw-btn-primary:hover { background: var(--primary-dark); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(79,70,229,.35); }
|
||||
.sw-btn-warning { background: var(--warning-light); color: var(--warning); border: 1px solid #FDE68A; }
|
||||
.sw-btn-warning:hover { background: var(--warning); color: #fff; }
|
||||
.sw-btn-danger { background: var(--danger-light); color: var(--danger); border: 1px solid #FECACA; }
|
||||
.sw-btn-danger:hover { background: var(--danger); color: #fff; }
|
||||
.sw-btn-sm { padding: 6px 12px; font-size: 12.5px; border-radius: 8px; }
|
||||
|
||||
.sw-alert { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-radius: var(--radius-md); font-size: 13.5px; font-weight: 500; margin-bottom: 20px; background: var(--success-light); color: var(--success); border: 1px solid #A7F3D0; }
|
||||
|
||||
.sw-card { background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); border: 1px solid var(--border); }
|
||||
.sw-table-card { overflow: hidden; }
|
||||
.sw-table-wrap { overflow-x: auto; }
|
||||
|
||||
table.sw-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
||||
table.sw-table thead tr { background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%); border-bottom: 2px solid #DDD6FE; }
|
||||
table.sw-table thead th { padding: 13px 16px; font-weight: 700; font-size: 12px; color: #5B21B6; text-transform: uppercase; letter-spacing: .6px; white-space: nowrap; border: none; }
|
||||
table.sw-table tbody tr { border-bottom: 1px solid var(--border); transition: background .15s; }
|
||||
table.sw-table tbody tr:last-child { border-bottom: none; }
|
||||
table.sw-table tbody tr:hover { background: #FAFAFF; }
|
||||
table.sw-table td { padding: 13px 16px; color: var(--text-mid); border: none; vertical-align: middle; }
|
||||
table.sw-table td.center { text-align: center; }
|
||||
table.sw-table td.name-cell { font-weight: 600; color: var(--text-dark); }
|
||||
|
||||
.sw-badge { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 99px; font-size: 11.5px; font-weight: 600; letter-spacing: .1px; }
|
||||
.sw-badge-info { background: var(--info-light); color: var(--info); }
|
||||
|
||||
.sw-action-cell { display: flex; gap: 6px; align-items: center; justify-content: center; }
|
||||
|
||||
.sw-no { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; background: #F3F4F6; border-radius: 7px; font-size: 12px; font-weight: 600; color: var(--text-soft); }
|
||||
|
||||
.sw-empty { padding: 60px 20px; text-align: center; color: var(--text-soft); }
|
||||
.sw-empty-icon { font-size: 40px; margin-bottom: 12px; opacity: .4; }
|
||||
.sw-empty p { font-size: 14px; margin: 0; }
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<div class="sw-topbar-left">
|
||||
<h3>Data Kelas</h3>
|
||||
<p>Pengelolaan data kelas SMA Negeri 1 Bondowoso</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.kelas.create') }}" class="sw-btn sw-btn-primary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
|
||||
Tambah Kelas
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
<div class="sw-alert">
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Card -->
|
||||
<div class="card shadow mb-4">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Tabel Data Kelas</h6>
|
||||
|
||||
<a href="{{ route('admin.kelas.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-plus"></i> Tambah Kelas
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
|
||||
<table class="table table-bordered table-hover">
|
||||
|
||||
<thead class="thead-light text-center">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama Kelas</th>
|
||||
<th>Jurusan</th>
|
||||
<th width="150px">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($kelas as $k)
|
||||
<tr>
|
||||
<td class="text-center">{{ $loop->iteration }}</td>
|
||||
<td>{{ $k->nama_kelas }}</td>
|
||||
<td class="text-center">
|
||||
<span class="badge badge-info">
|
||||
{{ $k->jurusan }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
|
||||
<a href="{{ route('admin.kelas.edit', $k->id) }}"
|
||||
class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-edit"></i>
|
||||
{{-- TABLE --}}
|
||||
<div class="sw-card sw-table-card">
|
||||
<div class="sw-table-wrap">
|
||||
<table class="sw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="center" style="width:52px">No</th>
|
||||
<th>Nama Kelas</th>
|
||||
<th class="center">Jurusan</th>
|
||||
<th class="center" style="width:160px">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($kelas as $k)
|
||||
<tr>
|
||||
<td class="center">
|
||||
<span class="sw-no">{{ $loop->iteration }}</span>
|
||||
</td>
|
||||
<td class="name-cell">{{ $k->nama_kelas }}</td>
|
||||
<td class="center">
|
||||
<span class="sw-badge sw-badge-info">{{ $k->jurusan }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="sw-action-cell">
|
||||
<a href="{{ route('admin.kelas.edit', $k->id) }}"
|
||||
class="sw-btn sw-btn-warning sw-btn-sm">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<form action="{{ route('admin.kelas.destroy', $k->id) }}"
|
||||
method="POST"
|
||||
class="d-inline">
|
||||
<form action="{{ route('admin.kelas.destroy', $k->id) }}"
|
||||
method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"
|
||||
<button type="button"
|
||||
class="sw-btn sw-btn-danger sw-btn-sm"
|
||||
onclick="return confirm('Yakin hapus data kelas ini?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/></svg>
|
||||
Hapus
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted">
|
||||
Data kelas belum tersedia
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="sw-empty">
|
||||
<div class="sw-empty-icon">🏫</div>
|
||||
<p>Data kelas belum tersedia</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,38 +1,231 @@
|
|||
{{-- ============================================================
|
||||
FILE 2: resources/views/admin/mapel/create.blade.php
|
||||
============================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h3>Tambah Mata Pelajaran</h3>
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
@foreach ($errors->all() as $error)
|
||||
<div>{{ $error }}</div>
|
||||
@endforeach
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
|
||||
:root {
|
||||
--brand: #1a56db;
|
||||
--brand-light: #e8f0fe;
|
||||
--brand-dark: #1648c0;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fee2e2;
|
||||
--page-bg: #f4f6fa;
|
||||
--surface: #ffffff;
|
||||
--border: rgba(0,0,0,0.08);
|
||||
--border-focus: rgba(26,86,219,0.5);
|
||||
--text-1: #111827;
|
||||
--text-2: #6b7280;
|
||||
--text-3: #9ca3af;
|
||||
--r-sm: 8px; --r-md: 12px; --r-lg: 16px;
|
||||
--sh-sm: 0 1px 4px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
||||
}
|
||||
.container,
|
||||
.container-fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
*, *::before, *::after { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.form-wrap {
|
||||
background: var(--page-bg);
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 28px 32px 56px;
|
||||
}
|
||||
|
||||
/* breadcrumb / back nav */
|
||||
.form-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
|
||||
.form-nav a { font-size: 13px; font-weight: 500; color: var(--text-2); text-decoration: none; display: inline-flex; align-items: center; gap: 5px; transition: color .12s; }
|
||||
.form-nav a:hover { color: var(--brand); }
|
||||
.form-nav-sep { color: var(--text-3); font-size: 13px; }
|
||||
.form-nav-current { font-size: 13px; font-weight: 600; color: var(--text-1); }
|
||||
|
||||
/* page title */
|
||||
.form-page-title { margin-bottom: 24px; }
|
||||
.form-page-title .eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--brand); margin-bottom: 4px; }
|
||||
.form-page-title h1 { font-size: 24px; font-weight: 700; color: var(--text-1); margin: 0; letter-spacing: -.4px; }
|
||||
|
||||
/* card */
|
||||
.form-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--r-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: var(--sh-sm);
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.form-card-header { padding: 20px 24px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
|
||||
.form-card-icon { width: 38px; height: 38px; border-radius: 9px; background: var(--brand-light); color: var(--brand); display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
|
||||
.form-card-header-text h5 { font-size: 15px; font-weight: 700; color: var(--text-1); margin: 0 0 2px; }
|
||||
.form-card-header-text p { font-size: 12px; color: var(--text-2); margin: 0; }
|
||||
.form-card-body { padding: 24px; }
|
||||
|
||||
/* error list */
|
||||
.form-errors { background: #fff5f5; border: 1px solid rgba(220,38,38,.2); border-left: 4px solid var(--danger); border-radius: var(--r-md); padding: 14px 16px; margin-bottom: 22px; }
|
||||
.form-errors ul { margin: 0; padding-left: 18px; }
|
||||
.form-errors li { font-size: 13px; color: var(--danger); font-weight: 500; line-height: 1.7; }
|
||||
|
||||
/* field */
|
||||
.field { margin-bottom: 20px; }
|
||||
.field:last-of-type { margin-bottom: 0; }
|
||||
.field-label { display: flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; color: var(--text-1); margin-bottom: 7px; }
|
||||
.field-label .req { color: var(--danger); font-size: 14px; line-height: 1; }
|
||||
.field-hint { font-size: 12px; color: var(--text-3); margin-top: 5px; }
|
||||
|
||||
.field-input {
|
||||
width: 100%; padding: 10px 13px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
font-size: 14px; color: var(--text-1);
|
||||
background: #fff;
|
||||
outline: none;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.field-input::placeholder { color: var(--text-3); }
|
||||
.field-input:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(26,86,219,.1); }
|
||||
.field-input.is-invalid { border-color: var(--danger); }
|
||||
.field-input.is-invalid:focus { box-shadow: 0 0 0 3px rgba(220,38,38,.1); }
|
||||
.field-input[type="number"] { font-family: 'DM Mono', monospace; }
|
||||
|
||||
/* input with prefix/suffix */
|
||||
.field-input-group { display: flex; align-items: stretch; border: 1.5px solid var(--border); border-radius: var(--r-sm); overflow: hidden; transition: border-color .15s, box-shadow .15s; background: #fff; }
|
||||
.field-input-group:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(26,86,219,.1); }
|
||||
.field-input-group .input-addon { display: flex; align-items: center; padding: 0 12px; background: #f8fafc; border-right: 1.5px solid var(--border); font-size: 12px; font-weight: 600; color: var(--text-3); white-space: nowrap; }
|
||||
.field-input-group .input-addon-right { border-right: none; border-left: 1.5px solid var(--border); }
|
||||
.field-input-group .field-input { border: none; border-radius: 0; box-shadow: none; }
|
||||
.field-input-group .field-input:focus { box-shadow: none; }
|
||||
|
||||
/* form footer */
|
||||
.form-footer { display: flex; align-items: center; gap: 10px; padding-top: 20px; border-top: 1px solid var(--border); margin-top: 24px; }
|
||||
|
||||
.btn-submit {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: var(--brand); color: #fff; border: none;
|
||||
border-radius: var(--r-sm); padding: 10px 22px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .15s ease;
|
||||
box-shadow: 0 2px 8px rgba(26,86,219,.25);
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.btn-submit:hover { background: var(--brand-dark); transform: translateY(-1px); color: #fff; }
|
||||
.btn-submit i { font-size: 11px; }
|
||||
|
||||
.btn-back {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: #f8fafc; color: var(--text-1);
|
||||
border: 1.5px solid rgba(0,0,0,.12);
|
||||
border-radius: var(--r-sm); padding: 10px 18px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .12s ease;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.btn-back:hover { background: #eef2f6; color: var(--text-1); }
|
||||
.btn-back i { font-size: 11px; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.form-wrap { padding: 20px 16px 40px; }
|
||||
.form-card { max-width: 100%; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="form-wrap">
|
||||
|
||||
{{-- BREADCRUMB --}}
|
||||
<div class="form-nav">
|
||||
<a href="{{ route('admin.mapel.index') }}">
|
||||
<i class="fas fa-arrow-left" style="font-size:11px;"></i> Mata Pelajaran
|
||||
</a>
|
||||
<span class="form-nav-sep">/</span>
|
||||
<span class="form-nav-current">Tambah Baru</span>
|
||||
</div>
|
||||
|
||||
<div class="form-page-title">
|
||||
<div class="eyebrow">Kurikulum</div>
|
||||
<h1>Tambah Mata Pelajaran</h1>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="form-card-icon"><i class="fas fa-book"></i></div>
|
||||
<div class="form-card-header-text">
|
||||
<h5>Data Mata Pelajaran</h5>
|
||||
<p>Isi semua field yang bertanda <span style="color:#dc2626;">*</span> wajib diisi</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="form-card-body">
|
||||
|
||||
<form action="{{ route('admin.mapel.store') }}" method="POST">
|
||||
@csrf
|
||||
@if ($errors->any())
|
||||
<div class="form-errors">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Nama Mapel</label>
|
||||
<input type="text" name="nama_mapel" class="form-control" required>
|
||||
<form action="{{ route('admin.mapel.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
{{-- Nama Mapel --}}
|
||||
<div class="field">
|
||||
<label class="field-label">
|
||||
Nama Mata Pelajaran <span class="req">*</span>
|
||||
</label>
|
||||
<input type="text" name="nama_mapel" value="{{ old('nama_mapel') }}"
|
||||
class="field-input {{ $errors->has('nama_mapel') ? 'is-invalid' : '' }}"
|
||||
placeholder="cth. Matematika Wajib" required>
|
||||
</div>
|
||||
|
||||
{{-- Kode Mapel --}}
|
||||
<div class="field">
|
||||
<label class="field-label">
|
||||
Kode Mata Pelajaran <span class="req">*</span>
|
||||
</label>
|
||||
<div class="field-input-group">
|
||||
<span class="input-addon"><i class="fas fa-hashtag" style="font-size:11px;"></i></span>
|
||||
<input type="text" name="kode_mapel" value="{{ old('kode_mapel') }}"
|
||||
class="field-input {{ $errors->has('kode_mapel') ? 'is-invalid' : '' }}"
|
||||
placeholder="cth. MTK-01" required>
|
||||
</div>
|
||||
<div class="field-hint">Gunakan kode unik untuk setiap mata pelajaran.</div>
|
||||
</div>
|
||||
|
||||
{{-- Jam Pelajaran --}}
|
||||
<div class="field">
|
||||
<label class="field-label">
|
||||
Jam Pelajaran <span class="req">*</span>
|
||||
</label>
|
||||
<div class="field-input-group">
|
||||
<input type="number" name="jam_pelajaran" value="{{ old('jam_pelajaran') }}"
|
||||
class="field-input {{ $errors->has('jam_pelajaran') ? 'is-invalid' : '' }}"
|
||||
placeholder="0" min="1" max="40" required>
|
||||
<span class="input-addon input-addon-right">jam / minggu</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn-submit">
|
||||
<i class="fas fa-save"></i> Simpan
|
||||
</button>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="btn-back">
|
||||
<i class="fas fa-times"></i> Batal
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Kode Mapel</label>
|
||||
<input type="text" name="kode_mapel" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Jam Pelajaran</label>
|
||||
<input type="number" name="jam_pelajaran" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Simpan</button>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="btn btn-secondary">Kembali</a>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,37 +1,213 @@
|
|||
{{-- ============================================================
|
||||
FILE 3: resources/views/admin/mapel/edit.blade.php
|
||||
============================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Edit Mata Pelajaran</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--brand: #1a56db;
|
||||
--brand-light: #e8f0fe;
|
||||
--brand-dark: #1648c0;
|
||||
--warning: #d97706;
|
||||
--warning-light:#fef3c7;
|
||||
--danger: #dc2626;
|
||||
--page-bg: #f4f6fa;
|
||||
--surface: #ffffff;
|
||||
--border: rgba(0,0,0,0.08);
|
||||
--text-1: #111827;
|
||||
--text-2: #6b7280;
|
||||
--text-3: #9ca3af;
|
||||
--r-sm: 8px; --r-md: 12px; --r-lg: 16px;
|
||||
--sh-sm: 0 1px 4px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
||||
}
|
||||
.container,
|
||||
.container-fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
*, *::before, *::after { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.form-wrap { background: var(--page-bg); min-height: 100vh; padding: 28px 32px 56px; }
|
||||
|
||||
.form-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
|
||||
.form-nav a { font-size: 13px; font-weight: 500; color: var(--text-2); text-decoration: none; display: inline-flex; align-items: center; gap: 5px; transition: color .12s; }
|
||||
.form-nav a:hover { color: var(--brand); }
|
||||
.form-nav-sep { color: var(--text-3); font-size: 13px; }
|
||||
.form-nav-current { font-size: 13px; font-weight: 600; color: var(--text-1); }
|
||||
|
||||
.form-page-title { margin-bottom: 24px; }
|
||||
.form-page-title .eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--warning); margin-bottom: 4px; }
|
||||
.form-page-title h1 { font-size: 24px; font-weight: 700; color: var(--text-1); margin: 0; letter-spacing: -.4px; }
|
||||
|
||||
/* edit indicator banner */
|
||||
.edit-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background: var(--warning-light);
|
||||
border: 1px solid rgba(217,119,6,.2);
|
||||
border-left: 4px solid var(--warning);
|
||||
border-radius: var(--r-md);
|
||||
padding: 13px 16px;
|
||||
margin-bottom: 22px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #92400e;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--r-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: var(--sh-sm);
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.form-card-header { padding: 20px 24px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
|
||||
.form-card-icon { width: 38px; height: 38px; border-radius: 9px; background: var(--warning-light); color: var(--warning); display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
|
||||
.form-card-header-text h5 { font-size: 15px; font-weight: 700; color: var(--text-1); margin: 0 0 2px; }
|
||||
.form-card-header-text p { font-size: 12px; color: var(--text-2); margin: 0; }
|
||||
.form-card-body { padding: 24px; }
|
||||
|
||||
.field { margin-bottom: 20px; }
|
||||
.field-label { display: flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; color: var(--text-1); margin-bottom: 7px; }
|
||||
.field-hint { font-size: 12px; color: var(--text-3); margin-top: 5px; }
|
||||
|
||||
.field-input {
|
||||
width: 100%; padding: 10px 13px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
font-size: 14px; color: var(--text-1);
|
||||
background: #fff; outline: none;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.field-input::placeholder { color: var(--text-3); }
|
||||
.field-input:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(26,86,219,.1); }
|
||||
.field-input[type="number"] { font-family: 'DM Mono', monospace; }
|
||||
|
||||
.field-input-group { display: flex; align-items: stretch; border: 1.5px solid var(--border); border-radius: var(--r-sm); overflow: hidden; transition: border-color .15s, box-shadow .15s; background: #fff; }
|
||||
.field-input-group:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(26,86,219,.1); }
|
||||
.field-input-group .input-addon { display: flex; align-items: center; padding: 0 12px; background: #f8fafc; border-right: 1.5px solid var(--border); font-size: 12px; font-weight: 600; color: var(--text-3); white-space: nowrap; }
|
||||
.field-input-group .input-addon-right { border-right: none; border-left: 1.5px solid var(--border); }
|
||||
.field-input-group .field-input { border: none; border-radius: 0; box-shadow: none; }
|
||||
.field-input-group .field-input:focus { box-shadow: none; }
|
||||
|
||||
.form-footer { display: flex; align-items: center; gap: 10px; padding-top: 20px; border-top: 1px solid var(--border); margin-top: 24px; }
|
||||
|
||||
.btn-submit {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: var(--warning); color: #fff; border: none;
|
||||
border-radius: var(--r-sm); padding: 10px 22px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .15s ease;
|
||||
box-shadow: 0 2px 8px rgba(217,119,6,.3);
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.btn-submit:hover { background: #b45309; transform: translateY(-1px); color: #fff; }
|
||||
.btn-submit i { font-size: 11px; }
|
||||
|
||||
.btn-back {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: #f8fafc; color: var(--text-1);
|
||||
border: 1.5px solid rgba(0,0,0,.12);
|
||||
border-radius: var(--r-sm); padding: 10px 18px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .12s ease;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.btn-back:hover { background: #eef2f6; color: var(--text-1); }
|
||||
.btn-back i { font-size: 11px; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.form-wrap { padding: 20px 16px 40px; }
|
||||
.form-card { max-width: 100%; }
|
||||
.edit-banner { max-width: 100%; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="form-wrap">
|
||||
|
||||
<div class="form-nav">
|
||||
<a href="{{ route('admin.mapel.index') }}">
|
||||
<i class="fas fa-arrow-left" style="font-size:11px;"></i> Mata Pelajaran
|
||||
</a>
|
||||
<span class="form-nav-sep">/</span>
|
||||
<span class="form-nav-current">Edit</span>
|
||||
</div>
|
||||
|
||||
<div class="form-page-title">
|
||||
<div class="eyebrow">Kurikulum</div>
|
||||
<h1>Edit Mata Pelajaran</h1>
|
||||
</div>
|
||||
|
||||
<div class="edit-banner">
|
||||
<i class="fas fa-pen" style="font-size:14px; flex-shrink:0;"></i>
|
||||
Anda sedang mengedit: <strong> {{ $mapel->nama_mapel }}</strong>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="form-card-header">
|
||||
<div class="form-card-icon"><i class="fas fa-pen"></i></div>
|
||||
<div class="form-card-header-text">
|
||||
<h5>Ubah Data Mata Pelajaran</h5>
|
||||
<p>Perubahan akan langsung tersimpan ke sistem</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-card-body">
|
||||
|
||||
<form action="{{ route('admin.mapel.update', $mapel->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nama Mapel</label>
|
||||
<input type="text" name="nama_mapel" value="{{ $mapel->nama_mapel }}" class="form-control">
|
||||
{{-- Nama Mapel --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Nama Mata Pelajaran</label>
|
||||
<input type="text" name="nama_mapel" value="{{ $mapel->nama_mapel }}"
|
||||
class="field-input" placeholder="cth. Matematika Wajib">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Kode Mapel</label>
|
||||
<input type="text" name="kode_mapel" value="{{ $mapel->kode_mapel }}" class="form-control">
|
||||
{{-- Kode Mapel --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Kode Mata Pelajaran</label>
|
||||
<div class="field-input-group">
|
||||
<span class="input-addon"><i class="fas fa-hashtag" style="font-size:11px;"></i></span>
|
||||
<input type="text" name="kode_mapel" value="{{ $mapel->kode_mapel }}"
|
||||
class="field-input" placeholder="cth. MTK-01">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Jam Pelajaran</label>
|
||||
<input type="number" name="jam_pelajaran" value="{{ $mapel->jam_pelajaran }}" class="form-control">
|
||||
{{-- Jam Pelajaran --}}
|
||||
<div class="field">
|
||||
<label class="field-label">Jam Pelajaran</label>
|
||||
<div class="field-input-group">
|
||||
<input type="number" name="jam_pelajaran" value="{{ $mapel->jam_pelajaran }}"
|
||||
class="field-input" placeholder="0" min="1" max="40">
|
||||
<span class="input-addon input-addon-right">jam / minggu</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Update</button>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="btn btn-secondary">Kembali</a>
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn-submit">
|
||||
<i class="fas fa-save"></i> Update
|
||||
</button>
|
||||
<a href="{{ route('admin.mapel.index') }}" class="btn-back">
|
||||
<i class="fas fa-arrow-left"></i> Kembali
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,84 +1,218 @@
|
|||
{{-- ============================================================
|
||||
FILE 1: resources/views/admin/mapel/index.blade.php
|
||||
============================================================ --}}
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page Heading -->
|
||||
<h1 class="h3 mb-2 text-gray-800">Data Mata Pelajaran</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');
|
||||
|
||||
<!-- Alert -->
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
{{ session('success') }}
|
||||
:root {
|
||||
--brand: #1a56db;
|
||||
--brand-light: #e8f0fe;
|
||||
--brand-dark: #1648c0;
|
||||
--success: #0d9488;
|
||||
--success-light:#ccfbf1;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fee2e2;
|
||||
--warning: #d97706;
|
||||
--warning-light:#fef3c7;
|
||||
--page-bg: #f4f6fa;
|
||||
--surface: #ffffff;
|
||||
--border: rgba(0,0,0,0.07);
|
||||
--border-md: rgba(0,0,0,0.12);
|
||||
--text-1: #111827;
|
||||
--text-2: #6b7280;
|
||||
--text-3: #9ca3af;
|
||||
--r-sm: 8px; --r-md: 12px; --r-lg: 16px;
|
||||
--sh-sm: 0 1px 4px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
||||
--sh-md: 0 4px 16px rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
.mp-wrap { background: var(--page-bg); min-height: 100vh; padding: 28px 32px 56px; }
|
||||
|
||||
/* header */
|
||||
.mp-header { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 24px; }
|
||||
.mp-header-left .eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--brand); margin-bottom: 4px; }
|
||||
.mp-header-left h1 { font-size: 24px; font-weight: 700; color: var(--text-1); margin: 0; letter-spacing: -.4px; }
|
||||
|
||||
/* primary button */
|
||||
.btn-primary-mp {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: var(--brand); color: #fff; border: none;
|
||||
border-radius: var(--r-sm); padding: 10px 20px;
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
text-decoration: none; transition: all .15s ease;
|
||||
box-shadow: 0 2px 8px rgba(26,86,219,.25); white-space: nowrap;
|
||||
}
|
||||
.btn-primary-mp:hover { background: var(--brand-dark); transform: translateY(-1px); color: #fff; box-shadow: 0 4px 12px rgba(26,86,219,.35); }
|
||||
.btn-primary-mp i { font-size: 11px; }
|
||||
|
||||
/* alert */
|
||||
.mp-alert {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
background: var(--success-light); border: 1px solid rgba(13,148,136,.2);
|
||||
border-left: 4px solid var(--success); border-radius: var(--r-md);
|
||||
padding: 13px 16px; margin-bottom: 20px;
|
||||
font-size: 13px; font-weight: 500; color: #065f46; position: relative;
|
||||
}
|
||||
.mp-alert-close { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; color: #065f46; font-size: 18px; opacity: .6; line-height: 1; }
|
||||
.mp-alert-close:hover { opacity: 1; }
|
||||
|
||||
/* card */
|
||||
.mp-card { background: var(--surface); border-radius: var(--r-lg); border: 1px solid var(--border); box-shadow: var(--sh-sm); overflow: hidden; }
|
||||
.mp-card-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 15px 20px; border-bottom: 1px solid var(--border); }
|
||||
.mp-count { font-size: 13px; font-weight: 600; color: var(--text-2); }
|
||||
.mp-count strong { color: var(--text-1); font-weight: 700; }
|
||||
|
||||
/* table */
|
||||
.mp-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
||||
.mp-table thead tr { background: #f8fafc; border-bottom: 1px solid var(--border); }
|
||||
.mp-table thead th { padding: 12px 16px; font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); white-space: nowrap; border: none; }
|
||||
.mp-table thead th:first-child { padding-left: 20px; }
|
||||
.mp-table thead th:last-child { padding-right: 20px; }
|
||||
.mp-table tbody tr { border-bottom: 1px solid var(--border); transition: background .12s; }
|
||||
.mp-table tbody tr:last-child { border-bottom: none; }
|
||||
.mp-table tbody tr:hover { background: #f8fafc; }
|
||||
.mp-table td { padding: 14px 16px; color: var(--text-1); vertical-align: middle; border: none; }
|
||||
.mp-table td:first-child { padding-left: 20px; }
|
||||
.mp-table td:last-child { padding-right: 20px; }
|
||||
|
||||
.cell-no { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-3); font-weight: 500; width: 44px; }
|
||||
.cell-name { font-weight: 600; color: var(--text-1); display: flex; align-items: center; gap: 10px; }
|
||||
.name-icon { width: 34px; height: 34px; border-radius: 8px; background: var(--brand-light); color: var(--brand); display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
|
||||
.cell-mono { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--text-2); }
|
||||
|
||||
/* badges */
|
||||
.pill { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; letter-spacing: .02em; }
|
||||
.pill-brand { background: var(--brand-light); color: var(--brand); border: 1px solid rgba(26,86,219,.15); }
|
||||
|
||||
/* jam display */
|
||||
.jam-wrap { display: inline-flex; align-items: baseline; gap: 4px; }
|
||||
.jam-num { font-size: 17px; font-weight: 700; font-family: 'DM Mono', monospace; color: var(--text-1); }
|
||||
.jam-unit { font-size: 11px; color: var(--text-3); font-weight: 500; }
|
||||
|
||||
/* action buttons */
|
||||
.action-group { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
|
||||
.btn-act {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 6px 12px; border-radius: 7px; font-size: 12px; font-weight: 600;
|
||||
cursor: pointer; text-decoration: none; border: 1px solid;
|
||||
transition: all .12s ease; white-space: nowrap;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif; background: none;
|
||||
}
|
||||
.btn-act i { font-size: 10px; }
|
||||
.btn-edit { background: #f8fafc; border-color: var(--border-md); color: var(--text-1); }
|
||||
.btn-edit:hover { background: #eef2f6; color: var(--text-1); }
|
||||
.btn-del { background: #fff5f5; border-color: rgba(220,38,38,.18); color: var(--danger); }
|
||||
.btn-del:hover { background: var(--danger-light); border-color: rgba(220,38,38,.35); color: var(--danger); }
|
||||
|
||||
/* empty */
|
||||
.empty-state { padding: 52px 24px; text-align: center; }
|
||||
.empty-icon { width: 54px; height: 54px; border-radius: 50%; background: #f1f5f9; display: flex; align-items: center; justify-content: center; margin: 0 auto 14px; color: var(--text-3); font-size: 22px; }
|
||||
.empty-state p { font-size: 14px; color: var(--text-2); font-weight: 500; margin: 0 0 4px; }
|
||||
.empty-state small { font-size: 12px; color: var(--text-3); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mp-wrap { padding: 20px 16px 40px; }
|
||||
.mp-header { flex-direction: column; align-items: flex-start; gap: 14px; }
|
||||
.mp-table-scroll { overflow-x: auto; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="mp-wrap">
|
||||
|
||||
<div class="mp-header">
|
||||
<div class="mp-header-left">
|
||||
<div class="eyebrow">Kurikulum</div>
|
||||
<h1>Mata Pelajaran</h1>
|
||||
</div>
|
||||
<a href="{{ route('admin.mapel.create') }}" class="btn-primary-mp">
|
||||
<i class="fa-solid fa-plus"></i> Tambah Mapel
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="mp-alert" id="mpAlert">
|
||||
<i class="fa-solid fa-check-circle" style="font-size:15px;flex-shrink:0;"></i>
|
||||
{{ session('success') }}
|
||||
<button class="mp-alert-close" onclick="document.getElementById('mpAlert').remove()">×</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Card -->
|
||||
<div class="card shadow mb-4">
|
||||
|
||||
<!-- Card Header -->
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Tabel Mata Pelajaran</h6>
|
||||
<a href="{{ route('admin.mapel.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-plus"></i> Tambah Data
|
||||
</a>
|
||||
<div class="mp-card">
|
||||
<div class="mp-card-toolbar">
|
||||
<div class="mp-count">Total <strong>{{ $mapel->count() }}</strong> mata pelajaran</div>
|
||||
</div>
|
||||
|
||||
<!-- Card Body -->
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover">
|
||||
|
||||
<thead class="thead-light">
|
||||
<tr class="text-center">
|
||||
<th>No</th>
|
||||
<th>Nama Mapel</th>
|
||||
<th>Kode</th>
|
||||
<th>Jam Pelajaran</th>
|
||||
<th width="150px">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($mapel as $m)
|
||||
<tr>
|
||||
<td class="text-center">{{ $loop->iteration }}</td>
|
||||
<td>{{ $m->nama_mapel }}</td>
|
||||
<td class="text-center">{{ $m->kode_mapel }}</td>
|
||||
<td class="text-center">{{ $m->jam_pelajaran }} Jam</td>
|
||||
<td class="text-center">
|
||||
|
||||
<a href="{{ route('admin.mapel.edit', $m->id) }}"
|
||||
class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-edit"></i>
|
||||
<div class="mp-table-scroll">
|
||||
<table class="mp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Kode</th>
|
||||
<th style="text-align:center;">Jam Pelajaran</th>
|
||||
<th style="text-align:right;">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($mapel as $m)
|
||||
<tr>
|
||||
<td class="cell-no">{{ str_pad($loop->iteration, 2, '0', STR_PAD_LEFT) }}</td>
|
||||
<td>
|
||||
<div class="cell-name">
|
||||
<div class="name-icon"><i class="fa-solid fa-book"></i></div>
|
||||
{{ $m->nama_mapel }}
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="pill pill-brand">{{ $m->kode_mapel }}</span></td>
|
||||
<td style="text-align:center;">
|
||||
<div class="jam-wrap">
|
||||
<span class="jam-num">{{ $m->jam_pelajaran }}</span>
|
||||
<span class="jam-unit">jam</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="action-group">
|
||||
<a href="{{ route('admin.mapel.edit', $m->id) }}" class="btn-act btn-edit">
|
||||
<i class="fa-solid fa-pen"></i> Edit
|
||||
</a>
|
||||
|
||||
<form action="{{ route('admin.mapel.destroy', $m->id) }}"
|
||||
method="POST"
|
||||
class="d-inline">
|
||||
<form action="{{ route('admin.mapel.destroy', $m->id) }}" method="POST" style="display:inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"
|
||||
<button type="submit" class="btn-act btn-del"
|
||||
onclick="return confirm('Yakin hapus data ini?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
<i class="fa-solid fa-trash"></i> Hapus
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
Data belum tersedia
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon"><i class="fa-solid fa-book"></i></div>
|
||||
<p>Belum ada mata pelajaran</p>
|
||||
<small>Klik "Tambah Mapel" untuk menambahkan data.</small>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,46 +1,404 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Profile Saya</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<form action="{{ route('admin.profile.update') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Nama</label>
|
||||
<input type="text" name="name" class="form-control"
|
||||
value="{{ auth()->user()->name }}">
|
||||
</div>
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 28px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 4px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Foto</label><br>
|
||||
/* ── ALERT ── */
|
||||
.sw-alert-success {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
background: var(--success-light); border: 1px solid #A7F3D0;
|
||||
border-radius: var(--radius-md); padding: 13px 16px;
|
||||
font-size: 13px; color: var(--success); margin-bottom: 24px; font-weight: 500;
|
||||
}
|
||||
|
||||
@if(auth()->user()->photo)
|
||||
<img src="{{ asset('uploads/' . auth()->user()->photo) }}"
|
||||
width="120" class="mb-2 rounded">
|
||||
@else
|
||||
<img src="{{ asset('sbadmin2/img/undraw_profile.svg') }}"
|
||||
width="120" class="mb-2">
|
||||
@endif
|
||||
/* ── GRID LAYOUT ── */
|
||||
.sw-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
<input type="file" name="photo" class="form-control mt-2">
|
||||
</div>
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<button class="btn btn-primary">Update Profil</button>
|
||||
/* ── AVATAR CARD ── */
|
||||
.avatar-card { text-align: center; }
|
||||
|
||||
</form>
|
||||
.avatar-banner {
|
||||
height: 90px;
|
||||
background: linear-gradient(135deg, #1E3A6E 0%, #4F46E5 60%, #7C3AED 100%);
|
||||
position: relative;
|
||||
}
|
||||
.avatar-banner::after {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.04'%3E%3Ccircle cx='30' cy='30' r='20'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
</div>
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-top: -50px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.avatar-img {
|
||||
width: 100px; height: 100px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 4px solid var(--surface);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,.12);
|
||||
display: block;
|
||||
}
|
||||
.avatar-online {
|
||||
position: absolute; bottom: 6px; right: 6px;
|
||||
width: 14px; height: 14px;
|
||||
background: var(--success);
|
||||
border: 2px solid var(--surface);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-body { padding: 0 24px 28px; }
|
||||
.avatar-name { font-size: 17px; font-weight: 800; color: var(--text-dark); margin-bottom: 4px; }
|
||||
.avatar-role {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
background: var(--primary-light); border: 1px solid #C7D2FE;
|
||||
border-radius: 99px; padding: 4px 12px;
|
||||
font-size: 11.5px; font-weight: 700; color: var(--primary);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.avatar-role span { width: 6px; height: 6px; background: var(--primary); border-radius: 50%; }
|
||||
|
||||
.avatar-stats {
|
||||
display: flex; gap: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-stat {
|
||||
flex: 1; padding: 12px 8px; text-align: center;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.avatar-stat:last-child { border-right: none; }
|
||||
.avatar-stat-val { font-size: 16px; font-weight: 800; color: var(--primary); }
|
||||
.avatar-stat-lbl { font-size: 10.5px; color: var(--text-soft); font-weight: 600; margin-top: 2px; }
|
||||
|
||||
/* ── FORM CARD ── */
|
||||
.sw-card-header {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 38px; height: 38px;
|
||||
background: var(--primary); border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(79,70,229,.3);
|
||||
}
|
||||
.sw-card-header h5 { font-size: 15px; font-weight: 700; color: #3730A3; margin: 0; }
|
||||
.sw-card-header p { font-size: 12.5px; color: #6D6AA4; margin: 2px 0 0; }
|
||||
|
||||
.sw-card-body { padding: 28px 24px; }
|
||||
|
||||
/* ── SECTION ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 11.5px; font-weight: 700; color: var(--primary);
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #C7D2FE, transparent);
|
||||
}
|
||||
|
||||
/* ── FORM ELEMENTS ── */
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 20px; }
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
|
||||
.sw-input-wrap { position: relative; }
|
||||
.sw-input-wrap svg {
|
||||
position: absolute; left: 14px; top: 50%;
|
||||
transform: translateY(-50%); color: #9CA3AF; pointer-events: none;
|
||||
}
|
||||
.sw-input {
|
||||
width: 100%;
|
||||
padding: 11px 14px 11px 42px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-input:focus {
|
||||
border-color: var(--primary);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
|
||||
}
|
||||
|
||||
/* ── PHOTO UPLOAD ZONE ── */
|
||||
.sw-upload-zone {
|
||||
border: 2px dashed var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 24px 20px;
|
||||
text-align: center;
|
||||
background: var(--neutral-light);
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
position: relative;
|
||||
}
|
||||
.sw-upload-zone:hover {
|
||||
border-color: var(--primary);
|
||||
background: var(--primary-light);
|
||||
}
|
||||
.sw-upload-zone input[type="file"] {
|
||||
position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
|
||||
}
|
||||
.sw-upload-icon {
|
||||
width: 44px; height: 44px;
|
||||
background: var(--primary-light); border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 12px; color: var(--primary);
|
||||
}
|
||||
.sw-upload-title { font-size: 13.5px; font-weight: 700; color: var(--text-dark); margin-bottom: 4px; }
|
||||
.sw-upload-sub { font-size: 12px; color: var(--text-soft); }
|
||||
.sw-upload-badge {
|
||||
display: inline-block; margin-top: 10px;
|
||||
background: var(--primary-light); border: 1px solid #C7D2FE;
|
||||
border-radius: 99px; padding: 3px 10px;
|
||||
font-size: 11px; font-weight: 600; color: var(--primary);
|
||||
}
|
||||
|
||||
/* current photo preview */
|
||||
.sw-photo-preview {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
background: var(--neutral-light); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md); padding: 14px 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.sw-photo-preview img {
|
||||
width: 52px; height: 52px;
|
||||
border-radius: 50%; object-fit: cover;
|
||||
border: 2px solid var(--primary-light);
|
||||
}
|
||||
.sw-photo-preview-info .title { font-size: 13px; font-weight: 700; color: var(--text-dark); }
|
||||
.sw-photo-preview-info .sub { font-size: 11.5px; color: var(--text-soft); margin-top: 2px; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
.sw-footer {
|
||||
display: flex; justify-content: flex-end; align-items: center;
|
||||
padding-top: 24px; border-top: 1px solid var(--border); margin-top: 8px;
|
||||
}
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 11px 22px; border-radius: var(--radius-md);
|
||||
font-size: 14px; font-weight: 700; border: none;
|
||||
cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.sw-btn-primary { background: var(--primary); color: #fff; box-shadow: 0 4px 14px rgba(79,70,229,.3); }
|
||||
.sw-btn-primary:hover { background: var(--primary-dark); transform: translateY(-1px); box-shadow: 0 6px 20px rgba(79,70,229,.38); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sw-grid { grid-template-columns: 1fr; }
|
||||
.sw-page { padding: 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<h3>Profile Saya</h3>
|
||||
<p>Kelola informasi dan foto profil akun Anda</p>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div class="sw-alert-success">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="sw-grid">
|
||||
|
||||
{{-- LEFT: AVATAR CARD --}}
|
||||
<div class="sw-card avatar-card">
|
||||
<div class="avatar-banner"></div>
|
||||
<div class="avatar-body">
|
||||
<div class="avatar-wrap">
|
||||
@if(auth()->user()->photo)
|
||||
<img src="{{ asset('uploads/' . auth()->user()->photo) }}" class="avatar-img" alt="Foto Profil">
|
||||
@else
|
||||
<img src="{{ asset('sbadmin2/img/undraw_profile.svg') }}" class="avatar-img" alt="Foto Profil">
|
||||
@endif
|
||||
<div class="avatar-online"></div>
|
||||
</div>
|
||||
|
||||
<div class="avatar-name">{{ auth()->user()->name }}</div>
|
||||
<div class="avatar-role">
|
||||
<span></span> Administrator
|
||||
</div>
|
||||
|
||||
<div class="avatar-stats">
|
||||
<div class="avatar-stat">
|
||||
<div class="avatar-stat-val">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:#4F46E5"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
</div>
|
||||
<div class="avatar-stat-lbl">Profil</div>
|
||||
</div>
|
||||
<div class="avatar-stat">
|
||||
<div class="avatar-stat-val">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:#059669"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
</div>
|
||||
<div class="avatar-stat-lbl">Aman</div>
|
||||
</div>
|
||||
<div class="avatar-stat">
|
||||
<div class="avatar-stat-val">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:#D97706"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
</div>
|
||||
<div class="avatar-stat-lbl">Aktif</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- RIGHT: FORM CARD --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Edit Profil</h5>
|
||||
<p>Perbarui nama dan foto profil Anda</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
<form action="{{ route('admin.profile.update') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
{{-- SECTION: INFO --}}
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Informasi Akun</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Nama Lengkap</label>
|
||||
<div class="sw-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
<input type="text" name="name" class="sw-input"
|
||||
value="{{ auth()->user()->name }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- SECTION: FOTO --}}
|
||||
<div class="sw-section" style="margin-top:28px;">
|
||||
<span class="sw-section-label">Foto Profil</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
{{-- Current photo preview --}}
|
||||
<div class="sw-photo-preview">
|
||||
@if(auth()->user()->photo)
|
||||
<img src="{{ asset('uploads/' . auth()->user()->photo) }}" alt="Foto saat ini">
|
||||
@else
|
||||
<img src="{{ asset('sbadmin2/img/undraw_profile.svg') }}" alt="Foto saat ini">
|
||||
@endif
|
||||
<div class="sw-photo-preview-info">
|
||||
<div class="title">Foto saat ini</div>
|
||||
<div class="sub">Format: JPG, PNG, WEBP · Maks. 2MB</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Upload zone --}}
|
||||
<div class="sw-upload-zone">
|
||||
<input type="file" name="photo" accept="image/*">
|
||||
<div class="sw-upload-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
</div>
|
||||
<div class="sw-upload-title">Klik untuk unggah foto baru</div>
|
||||
<div class="sw-upload-sub">atau seret & lepas file ke sini</div>
|
||||
<div class="sw-upload-badge">Pilih File</div>
|
||||
</div>
|
||||
|
||||
{{-- FOOTER --}}
|
||||
<div class="sw-footer">
|
||||
<button type="submit" class="sw-btn sw-btn-primary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Update Profil
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Live preview when file is selected
|
||||
const fileInput = document.querySelector('input[type="file"]');
|
||||
const previewImg = document.querySelector('.sw-photo-preview img');
|
||||
const uploadTitle = document.querySelector('.sw-upload-title');
|
||||
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
previewImg.src = e.target.result;
|
||||
uploadTitle.textContent = file.name;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,103 +1,301 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h2 class="mb-4 text-primary">Tambah Siswa</h2>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-light: #EEF2FF;
|
||||
--primary-dark: #3730A3;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
/* ── BREADCRUMB / TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 24px; }
|
||||
.sw-topbar h3 {
|
||||
font-size: 21px; font-weight: 700;
|
||||
color: var(--text-dark); margin: 0 0 4px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05), 0 1px 2px rgba(0,0,0,.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 38px; height: 38px;
|
||||
background: var(--primary);
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sw-card-header h5 {
|
||||
font-size: 15px; font-weight: 700;
|
||||
color: #3730A3; margin: 0;
|
||||
}
|
||||
.sw-card-header p { font-size: 12.5px; color: #6D6AA4; margin: 2px 0 0; }
|
||||
|
||||
.sw-card-body { padding: 28px 24px; }
|
||||
|
||||
/* ── SECTION DIVIDER ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 28px 0 20px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 12px; font-weight: 700;
|
||||
color: var(--primary); text-transform: uppercase; letter-spacing: .8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #C7D2FE, transparent);
|
||||
}
|
||||
|
||||
/* ── FORM GROUP ── */
|
||||
.sw-form-row { display: flex; gap: 18px; margin-bottom: 18px; }
|
||||
.sw-form-group { flex: 1; display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
|
||||
.sw-form-group.half { flex: 0 0 calc(50% - 9px); }
|
||||
.sw-label {
|
||||
font-size: 12.5px; font-weight: 600;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input, .sw-select {
|
||||
padding: 10px 14px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark);
|
||||
background: var(--neutral-light);
|
||||
outline: none;
|
||||
transition: border-color .15s, box-shadow .15s, background .15s;
|
||||
width: 100%;
|
||||
}
|
||||
.sw-input:focus, .sw-select:focus {
|
||||
border-color: var(--primary);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
/* ── ALERT ── */
|
||||
.sw-alert-info {
|
||||
display: flex; gap: 12px; align-items: flex-start;
|
||||
padding: 14px 16px;
|
||||
background: var(--info-light);
|
||||
border: 1px solid #BAE6FD;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--info);
|
||||
font-size: 13px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sw-alert-info svg { flex-shrink: 0; margin-top: 1px; }
|
||||
.sw-alert-info strong { font-weight: 700; }
|
||||
|
||||
.sw-alert-danger {
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
padding: 14px 16px;
|
||||
background: var(--danger-light);
|
||||
border: 1px solid #FECACA;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--danger);
|
||||
font-size: 13px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sw-alert-danger ul { margin: 0; padding-left: 16px; }
|
||||
.sw-alert-danger li { margin-bottom: 2px; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
.sw-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 8px;
|
||||
}
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
border: none; cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
}
|
||||
.sw-btn-secondary { background: var(--surface); color: var(--text-mid); border: 1.5px solid var(--border); }
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-mid); }
|
||||
.sw-btn-success { background: var(--success); color: #fff; }
|
||||
.sw-btn-success:hover { background: #047857; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(5,150,105,.3); }
|
||||
.sw-btn-success:disabled { opacity: .65; transform: none; box-shadow: none; cursor: not-allowed; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-form-row { flex-direction: column; gap: 0; }
|
||||
.sw-form-group.half { flex: 1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
<div class="sw-topbar">
|
||||
<h3>Tambah Siswa</h3>
|
||||
<p>Isi formulir di bawah untuk mendaftarkan siswa baru</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-card">
|
||||
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Formulir Data Siswa</h5>
|
||||
<p>Semua kolom bertanda <span style="color:#DC2626">*</span> wajib diisi</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul class="mb-0">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sw-alert-danger">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.siswa.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Nama</label>
|
||||
<input type="text" name="nama" class="form-control" required>
|
||||
</div>
|
||||
{{-- SECTION: DATA SISWA --}}
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Data Siswa</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>NIS</label>
|
||||
<input type="text" name="nis" class="form-control" required>
|
||||
<div class="sw-form-row">
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Nama <span>*</span></label>
|
||||
<input type="text" name="nama" class="sw-input" placeholder="Nama lengkap siswa" required>
|
||||
</div>
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">NIS <span>*</span></label>
|
||||
<input type="text" name="nis" class="sw-input" placeholder="Nomor Induk Siswa" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Jenis Kelamin</label>
|
||||
<select name="jenis_kelamin" class="form-control" required>
|
||||
<div class="sw-form-row">
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Jenis Kelamin <span>*</span></label>
|
||||
<select name="jenis_kelamin" class="sw-select" required>
|
||||
<option value="">-- Pilih --</option>
|
||||
<option value="L">Laki-laki</option>
|
||||
<option value="P">Perempuan</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Nama Orang Tua</label>
|
||||
<input type="text" name="nama_ortu" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<label>Kelas</label>
|
||||
<select name="kelas_id" class="form-control" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<label>Alamat</label>
|
||||
<input type="text" name="alamat" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<label>Telepon</label>
|
||||
<input type="text" name="telepon" class="form-control" required>
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Nama Orang Tua <span>*</span></label>
|
||||
<input type="text" name="nama_ortu" class="sw-input" placeholder="Nama ayah/ibu/wali" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h5 class="text-primary">Akun Login</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Email</label>
|
||||
<input type="email" name="email" class="form-control" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Kelas <span>*</span></label>
|
||||
<select name="kelas_id" class="sw-select" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- 🔥 INFO PASSWORD --}}
|
||||
<div class="alert alert-info">
|
||||
Password default: <strong>12345678</strong><br>
|
||||
Siswa wajib mengganti password saat login pertama.
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Alamat <span>*</span></label>
|
||||
<input type="text" name="alamat" class="sw-input" placeholder="Alamat lengkap" required>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('admin.siswa.index') }}" class="btn btn-secondary">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Telepon <span>*</span></label>
|
||||
<input type="text" name="telepon" class="sw-input" placeholder="Nomor telepon aktif" required>
|
||||
</div>
|
||||
|
||||
{{-- SECTION: AKUN LOGIN --}}
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Akun Login</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Email <span>*</span></label>
|
||||
<input type="email" name="email" class="sw-input" placeholder="email@siswa.com" required>
|
||||
</div>
|
||||
|
||||
<div class="sw-alert-info">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||||
<div>
|
||||
Password default: <strong>12345678</strong><br>
|
||||
Siswa wajib mengganti password saat login pertama.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FOOTER --}}
|
||||
<div class="sw-footer">
|
||||
<a href="{{ route('admin.siswa.index') }}" class="sw-btn sw-btn-secondary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn btn-success"
|
||||
onclick="this.disabled=true; this.innerText='Menyimpan...'; this.form.submit();">
|
||||
Simpan
|
||||
</button>
|
||||
<button type="submit" class="sw-btn sw-btn-success"
|
||||
onclick="this.disabled=true; this.innerText='Menyimpan...'; this.form.submit();">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Simpan Siswa
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,76 +1,263 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h2 class="mb-4 text-primary">Edit Siswa</h2>
|
||||
|
||||
<div class="card shadow-lg border-0">
|
||||
<div class="card-body">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-light: #EEF2FF;
|
||||
--primary-dark: #3730A3;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 24px; }
|
||||
.sw-topbar h3 {
|
||||
font-size: 21px; font-weight: 700;
|
||||
color: var(--text-dark); margin: 0 0 4px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05), 0 1px 2px rgba(0,0,0,.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #FFFBEB 0%, #FEF3C7 100%);
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 38px; height: 38px;
|
||||
background: var(--warning);
|
||||
border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.sw-card-header h5 {
|
||||
font-size: 15px; font-weight: 700;
|
||||
color: #92400E; margin: 0;
|
||||
}
|
||||
.sw-card-header p { font-size: 12.5px; color: #A16207; margin: 2px 0 0; }
|
||||
|
||||
/* ── IDENTITY CHIP ── */
|
||||
.sw-id-chip {
|
||||
margin-left: auto;
|
||||
background: var(--warning-light);
|
||||
border: 1px solid #FDE68A;
|
||||
border-radius: 99px;
|
||||
padding: 5px 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--warning);
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
|
||||
.sw-card-body { padding: 28px 24px; }
|
||||
|
||||
/* ── SECTION DIVIDER ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 28px 0 20px;
|
||||
}
|
||||
.sw-section:first-child { margin-top: 0; }
|
||||
.sw-section-label {
|
||||
font-size: 12px; font-weight: 700;
|
||||
color: var(--warning); text-transform: uppercase; letter-spacing: .8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #FDE68A, transparent);
|
||||
}
|
||||
|
||||
/* ── FORM ── */
|
||||
.sw-form-row { display: flex; gap: 18px; margin-bottom: 18px; }
|
||||
.sw-form-group { flex: 1; display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
|
||||
.sw-form-group.half { flex: 0 0 calc(50% - 9px); }
|
||||
.sw-label {
|
||||
font-size: 12.5px; font-weight: 600;
|
||||
color: var(--text-mid);
|
||||
}
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input, .sw-select {
|
||||
padding: 10px 14px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark);
|
||||
background: var(--neutral-light);
|
||||
outline: none;
|
||||
transition: border-color .15s, box-shadow .15s, background .15s;
|
||||
width: 100%;
|
||||
}
|
||||
.sw-input:focus, .sw-select:focus {
|
||||
border-color: var(--warning);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(217,119,6,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
/* ── ALERT DANGER ── */
|
||||
.sw-alert-danger {
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
padding: 14px 16px;
|
||||
background: var(--danger-light);
|
||||
border: 1px solid #FECACA;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--danger);
|
||||
font-size: 13px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sw-alert-danger ul { margin: 0; padding-left: 16px; }
|
||||
.sw-alert-danger li { margin-bottom: 2px; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
.sw-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 8px;
|
||||
}
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
border: none; cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
}
|
||||
.sw-btn-secondary {
|
||||
background: var(--surface); color: var(--text-mid);
|
||||
border: 1.5px solid var(--border);
|
||||
}
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-mid); }
|
||||
.sw-btn-warning { background: var(--warning); color: #fff; }
|
||||
.sw-btn-warning:hover { background: #B45309; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(217,119,6,.3); color: #fff; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-form-row { flex-direction: column; gap: 0; }
|
||||
.sw-form-group.half { flex: 1; }
|
||||
.sw-id-chip { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
<div class="sw-topbar">
|
||||
<h3>Edit Data Siswa</h3>
|
||||
<p>Perbarui informasi siswa yang terdaftar di sistem</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-card">
|
||||
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Edit Siswa</h5>
|
||||
<p>Semua kolom bertanda <span style="color:#DC2626">*</span> wajib diisi</p>
|
||||
</div>
|
||||
<div class="sw-id-chip">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M8 12h8M8 16h5"/></svg>
|
||||
NIS: {{ $siswa->nis }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul class="mb-0">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sw-alert-danger">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.siswa.update', $siswa->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
{{-- DATA SISWA --}}
|
||||
<h5 class="text-primary mb-3">Data Siswa</h5>
|
||||
{{-- SECTION: DATA SISWA --}}
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Data Siswa</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Nama</label>
|
||||
<div class="sw-form-row">
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Nama <span>*</span></label>
|
||||
<input type="text" name="nama"
|
||||
value="{{ old('nama', $siswa->nama) }}"
|
||||
class="form-control" required>
|
||||
class="sw-input" placeholder="Nama lengkap" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">NIS</label>
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">NIS <span>*</span></label>
|
||||
<input type="text" name="nis"
|
||||
value="{{ old('nis', $siswa->nis) }}"
|
||||
class="form-control" required>
|
||||
class="sw-input" placeholder="Nomor Induk Siswa" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{{-- JENIS KELAMIN --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Jenis Kelamin</label>
|
||||
<select name="jenis_kelamin" class="form-control" required>
|
||||
<div class="sw-form-row">
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Jenis Kelamin <span>*</span></label>
|
||||
<select name="jenis_kelamin" class="sw-select" required>
|
||||
<option value="">-- Pilih --</option>
|
||||
<option value="L"
|
||||
{{ old('jenis_kelamin', $siswa->jenis_kelamin) == 'L' ? 'selected' : '' }}>
|
||||
Laki-laki
|
||||
</option>
|
||||
<option value="P"
|
||||
{{ old('jenis_kelamin', $siswa->jenis_kelamin) == 'P' ? 'selected' : '' }}>
|
||||
Perempuan
|
||||
</option>
|
||||
<option value="L" {{ old('jenis_kelamin', $siswa->jenis_kelamin) == 'L' ? 'selected' : '' }}>Laki-laki</option>
|
||||
<option value="P" {{ old('jenis_kelamin', $siswa->jenis_kelamin) == 'P' ? 'selected' : '' }}>Perempuan</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- NAMA ORANG TUA --}}
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">Nama Orang Tua</label>
|
||||
<div class="sw-form-group half">
|
||||
<label class="sw-label">Nama Orang Tua <span>*</span></label>
|
||||
<input type="text" name="nama_ortu"
|
||||
value="{{ old('nama_ortu', $siswa->nama_ortu) }}"
|
||||
class="form-control" required>
|
||||
class="sw-input" placeholder="Nama ayah/ibu/wali" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- KELAS --}}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Kelas</label>
|
||||
<select name="kelas_id" class="form-control" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Kelas <span>*</span></label>
|
||||
<select name="kelas_id" class="sw-select" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
|
|
@ -81,30 +268,29 @@ class="form-control" required>
|
|||
</select>
|
||||
</div>
|
||||
|
||||
{{-- ALAMAT --}}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Alamat</label>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Alamat</label>
|
||||
<input type="text" name="alamat"
|
||||
value="{{ old('alamat', $siswa->alamat) }}"
|
||||
class="form-control">
|
||||
class="sw-input" placeholder="Alamat lengkap">
|
||||
</div>
|
||||
|
||||
{{-- TELEPON --}}
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Telepon</label>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Telepon</label>
|
||||
<input type="text" name="telepon"
|
||||
value="{{ old('telepon', $siswa->telepon) }}"
|
||||
class="form-control">
|
||||
class="sw-input" placeholder="Nomor telepon">
|
||||
</div>
|
||||
|
||||
{{-- BUTTON --}}
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.siswa.index') }}" class="btn btn-secondary">
|
||||
⬅️ Batal
|
||||
{{-- FOOTER --}}
|
||||
<div class="sw-footer">
|
||||
<a href="{{ route('admin.siswa.index') }}" class="sw-btn sw-btn-secondary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Batal
|
||||
</a>
|
||||
|
||||
<button class="btn btn-success">
|
||||
💾 Update
|
||||
<button type="submit" class="sw-btn sw-btn-warning">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Update Siswa
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -112,4 +298,5 @@ class="form-control">
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -3,301 +3,405 @@
|
|||
@section('content')
|
||||
|
||||
<style>
|
||||
/* ===== GLOBAL ===== */
|
||||
* {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-light: #EEF2FF;
|
||||
--primary-dark: #3730A3;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--neutral: #6B7280;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
|
||||
--shadow-md: 0 4px 16px rgba(0,0,0,.07);
|
||||
}
|
||||
|
||||
/* ===== CARD ===== */
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||
border: none;
|
||||
}
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
/* ===== HEADER ===== */
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
/* ── PAGE WRAPPER ── */
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
/* ===== FILTER ===== */
|
||||
.form-select {
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* ===== TABLE ===== */
|
||||
.table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
.sw-topbar-left h3 {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--text-dark);
|
||||
margin: 0;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
|
||||
/* HEADER GRADIENT */
|
||||
.table thead {
|
||||
background: linear-gradient(to right, #eef1f5, #e6ebf1);
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
border: none;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
/* BODY */
|
||||
.table tbody tr {
|
||||
background: #ffffff;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
.table tbody tr:nth-child(even) {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: #eef3f8;
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 14px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ===== BADGE ===== */
|
||||
.badge {
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge.bg-success {
|
||||
background: #e6f7ee !important;
|
||||
color: #1f9d55;
|
||||
}
|
||||
|
||||
.badge.bg-danger {
|
||||
background: #fdecea !important;
|
||||
color: #d93025;
|
||||
}
|
||||
|
||||
.badge.bg-primary {
|
||||
background: #e8f0fe !important;
|
||||
color: #1a73e8;
|
||||
}
|
||||
|
||||
.badge.bg-secondary {
|
||||
background: #f1f3f4 !important;
|
||||
color: #5f6368;
|
||||
}
|
||||
|
||||
.badge.bg-info {
|
||||
background: #e3f2fd !important;
|
||||
color: #0b5ed7;
|
||||
}
|
||||
|
||||
/* ===== BUTTON ===== */
|
||||
.btn-sm {
|
||||
padding: 6px 10px;
|
||||
.sw-topbar-left p {
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-soft);
|
||||
margin: 2px 0 0;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #d93025;
|
||||
/* ── BUTTON ── */
|
||||
.sw-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 9px 18px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: all .18s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
.sw-btn-primary { background: var(--primary); color: #fff; }
|
||||
.sw-btn-primary:hover { background: var(--primary-dark); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(79,70,229,.35); }
|
||||
.sw-btn-secondary { background: var(--surface); color: var(--text-mid); border: 1px solid var(--border); }
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-mid); }
|
||||
.sw-btn-danger { background: var(--danger-light); color: var(--danger); border: 1px solid #FECACA; }
|
||||
.sw-btn-danger:hover { background: var(--danger); color: #fff; }
|
||||
.sw-btn-warning { background: var(--warning-light); color: var(--warning); border: 1px solid #FDE68A; }
|
||||
.sw-btn-warning:hover { background: var(--warning); color: #fff; }
|
||||
.sw-btn-success { background: var(--success-light); color: var(--success); border: 1px solid #A7F3D0; }
|
||||
.sw-btn-success:hover { background: var(--success); color: #fff; }
|
||||
.sw-btn-sm { padding: 6px 12px; font-size: 12.5px; border-radius: 8px; }
|
||||
|
||||
/* ── ALERT ── */
|
||||
.sw-alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 13px 16px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
background: var(--success-light);
|
||||
color: var(--success);
|
||||
border: 1px solid #A7F3D0;
|
||||
}
|
||||
.sw-alert svg { flex-shrink: 0; }
|
||||
|
||||
/* ── FILTER CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.sw-filter {
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.sw-filter form { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
|
||||
.sw-filter label { font-size: 13px; font-weight: 600; color: var(--text-mid); white-space: nowrap; }
|
||||
.sw-filter .form-select {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
color: var(--text-mid);
|
||||
background: var(--neutral-light);
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.sw-filter .form-select:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
|
||||
|
||||
/* ── BULK BAR ── */
|
||||
.sw-bulk-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* ── TABLE CARD ── */
|
||||
.sw-table-card { overflow: hidden; }
|
||||
.sw-table-wrap { overflow-x: auto; }
|
||||
|
||||
table.sw-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
table.sw-table thead tr {
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
border-bottom: 2px solid #DDD6FE;
|
||||
}
|
||||
table.sw-table thead th {
|
||||
padding: 13px 16px;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
color: #5B21B6;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .6px;
|
||||
white-space: nowrap;
|
||||
border: none;
|
||||
}
|
||||
table.sw-table tbody tr {
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background .15s;
|
||||
}
|
||||
table.sw-table tbody tr:last-child { border-bottom: none; }
|
||||
table.sw-table tbody tr:hover { background: #FAFAFF; }
|
||||
table.sw-table td {
|
||||
padding: 13px 16px;
|
||||
color: var(--text-mid);
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
table.sw-table td.center { text-align: center; }
|
||||
table.sw-table td.name-cell { font-weight: 600; color: var(--text-dark); }
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #b1271b;
|
||||
/* ── CUSTOM CHECKBOX ── */
|
||||
.sw-check {
|
||||
width: 16px; height: 16px;
|
||||
accent-color: var(--primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td .btn {
|
||||
min-width: 80px;
|
||||
/* ── BADGES ── */
|
||||
.sw-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 99px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: .1px;
|
||||
}
|
||||
.sw-badge-success { background: var(--success-light); color: var(--success); }
|
||||
.sw-badge-danger { background: var(--danger-light); color: var(--danger); }
|
||||
.sw-badge-primary { background: var(--primary-light); color: var(--primary); }
|
||||
.sw-badge-neutral { background: #F3F4F6; color: #6B7280; }
|
||||
.sw-badge-info { background: var(--info-light); color: var(--info); }
|
||||
|
||||
/* ── NONAKTIF INLINE FORM ── */
|
||||
.sw-nonaktif-form {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
.sw-nonaktif-input {
|
||||
width: 110px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 7px;
|
||||
font-size: 12px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-mid);
|
||||
background: var(--neutral-light);
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.sw-nonaktif-input:focus { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(220,38,38,.08); }
|
||||
.sw-nonaktif-input::placeholder { color: #9CA3AF; }
|
||||
|
||||
/* ── ACTION CELL ── */
|
||||
.sw-action-cell { display: flex; gap: 6px; align-items: center; justify-content: center; }
|
||||
|
||||
/* ── EMPTY STATE ── */
|
||||
.sw-empty {
|
||||
padding: 60px 20px;
|
||||
text-align: center;
|
||||
color: var(--text-soft);
|
||||
}
|
||||
.sw-empty-icon { font-size: 40px; margin-bottom: 12px; opacity: .4; }
|
||||
.sw-empty p { font-size: 14px; margin: 0; }
|
||||
|
||||
/* ── DOT INDICATOR ── */
|
||||
.sw-dot {
|
||||
display: inline-block;
|
||||
width: 7px; height: 7px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.sw-dot-success { background: var(--success); }
|
||||
.sw-dot-danger { background: var(--danger); }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="text-dark">Data Siswa</h3>
|
||||
<a href="{{ route('admin.siswa.create') }}" class="btn btn-primary">
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<div class="sw-topbar-left">
|
||||
<h3>Data Siswa</h3>
|
||||
<p>Kelola seluruh data dan status siswa</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.siswa.create') }}" class="sw-btn sw-btn-primary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
|
||||
Tambah Siswa
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
<div class="sw-alert">
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- FILTER --}}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<form method="GET" class="d-flex gap-2 align-items-center">
|
||||
|
||||
<select name="kelas_id" class="form-select w-auto">
|
||||
<option value="">Semua Kelas</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<button class="btn btn-primary">Filter</button>
|
||||
|
||||
<a href="{{ route('admin.siswa.index') }}" class="btn btn-secondary">
|
||||
Reset
|
||||
</a>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="sw-card sw-filter">
|
||||
<form method="GET">
|
||||
<label>Kelas</label>
|
||||
<select name="kelas_id" class="form-select">
|
||||
<option value="">Semua Kelas</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="sw-btn sw-btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
|
||||
Filter
|
||||
</button>
|
||||
<a href="{{ route('admin.siswa.index') }}" class="sw-btn sw-btn-secondary">Reset</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- BULK ACTION --}}
|
||||
{{-- BULK ACTION + TABLE --}}
|
||||
<form action="{{ route('admin.siswa.bulkNonaktif') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<button class="btn btn-danger mb-3"
|
||||
onclick="return confirm('Nonaktifkan siswa yang dipilih?')">
|
||||
Nonaktifkan Terpilih
|
||||
</button>
|
||||
|
||||
{{-- TABLE --}}
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle text-center">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30">
|
||||
<input type="checkbox" id="checkAll">
|
||||
</th>
|
||||
<th>No</th>
|
||||
<th>Nama</th>
|
||||
<th>JK</th>
|
||||
<th>NIS</th>
|
||||
<th>Kelas</th>
|
||||
<th>Orang Tua</th>
|
||||
<th>Email</th>
|
||||
<th>Alamat</th>
|
||||
<th>Telepon</th>
|
||||
<th>Status</th>
|
||||
<th width="200">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($siswa as $item)
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<input type="checkbox" name="ids[]" value="{{ $item->id }}">
|
||||
</td>
|
||||
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
|
||||
<td class="text-start">{{ $item->nama }}</td>
|
||||
|
||||
<td>
|
||||
@if($item->jenis_kelamin == 'L')
|
||||
<span class="badge bg-primary">L</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">P</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td>{{ $item->nis }}</td>
|
||||
|
||||
<td>
|
||||
<span class="badge bg-info">
|
||||
{{ $item->kelas->nama_kelas ?? '-' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="text-start">{{ $item->nama_ortu }}</td>
|
||||
|
||||
<td>{{ $item->user->email ?? '-' }}</td>
|
||||
|
||||
<td class="text-start">{{ $item->alamat }}</td>
|
||||
|
||||
<td>{{ $item->telepon }}</td>
|
||||
|
||||
<td>
|
||||
@if($item->status == 'aktif')
|
||||
<span class="badge bg-success">Aktif</span>
|
||||
@else
|
||||
<span class="badge bg-danger">Nonaktif</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="d-flex justify-content-center gap-2">
|
||||
|
||||
<a href="{{ route('admin.siswa.edit', $item->id) }}"
|
||||
class="btn btn-warning btn-sm">
|
||||
Edit
|
||||
</a>
|
||||
|
||||
@if($item->status == 'aktif')
|
||||
<form action="{{ route('admin.siswa.nonaktif', $item->id) }}" method="POST" class="d-flex gap-1">
|
||||
@csrf
|
||||
|
||||
<input type="text" name="alasan"
|
||||
placeholder="Alasan..."
|
||||
required
|
||||
style="width:120px; font-size:12px;">
|
||||
|
||||
<button class="btn btn-danger btn-sm">
|
||||
Nonaktif
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('admin.siswa.aktifkan', $item->id) }}"
|
||||
class="btn btn-success btn-sm">
|
||||
Aktifkan
|
||||
</a>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="12" class="text-muted">
|
||||
Tidak ada data siswa
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="sw-bulk-bar">
|
||||
<button type="submit" class="sw-btn sw-btn-danger"
|
||||
onclick="return confirm('Nonaktifkan siswa yang dipilih?')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
|
||||
Nonaktifkan Terpilih
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sw-card sw-table-card">
|
||||
<div class="sw-table-wrap">
|
||||
<table class="sw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="center" style="width:36px">
|
||||
<input type="checkbox" id="checkAll" class="sw-check">
|
||||
</th>
|
||||
<th class="center" style="width:44px">No</th>
|
||||
<th>Nama</th>
|
||||
<th class="center">JK</th>
|
||||
<th class="center">NIS</th>
|
||||
<th class="center">Kelas</th>
|
||||
<th>Orang Tua</th>
|
||||
<th>Email</th>
|
||||
<th>Alamat</th>
|
||||
<th class="center">Telepon</th>
|
||||
<th class="center">Status</th>
|
||||
<th class="center" style="width:220px">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($siswa as $item)
|
||||
<tr>
|
||||
<td class="center">
|
||||
<input type="checkbox" name="ids[]" value="{{ $item->id }}" class="sw-check">
|
||||
</td>
|
||||
<td class="center" style="color:var(--text-soft); font-size:12px;">
|
||||
{{ $loop->iteration }}
|
||||
</td>
|
||||
<td class="name-cell">{{ $item->nama }}</td>
|
||||
<td class="center">
|
||||
@if($item->jenis_kelamin == 'L')
|
||||
<span class="sw-badge sw-badge-primary">L</span>
|
||||
@else
|
||||
<span class="sw-badge sw-badge-neutral">P</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="center" style="font-variant-numeric:tabular-nums; font-size:13px;">
|
||||
{{ $item->nis }}
|
||||
</td>
|
||||
<td class="center">
|
||||
<span class="sw-badge sw-badge-info">{{ $item->kelas->nama_kelas ?? '-' }}</span>
|
||||
</td>
|
||||
<td>{{ $item->nama_ortu }}</td>
|
||||
<td style="font-size:12.5px; color:var(--text-soft);">{{ $item->user->email ?? '-' }}</td>
|
||||
<td style="max-width:160px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" title="{{ $item->alamat }}">
|
||||
{{ $item->alamat }}
|
||||
</td>
|
||||
<td class="center" style="font-size:12.5px;">{{ $item->telepon }}</td>
|
||||
<td class="center">
|
||||
@if($item->status == 'aktif')
|
||||
<span class="sw-badge sw-badge-success">
|
||||
<span class="sw-dot sw-dot-success"></span>Aktif
|
||||
</span>
|
||||
@else
|
||||
<span class="sw-badge sw-badge-danger">
|
||||
<span class="sw-dot sw-dot-danger"></span>Nonaktif
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<div class="sw-action-cell">
|
||||
<a href="{{ route('admin.siswa.edit', $item->id) }}"
|
||||
class="sw-btn sw-btn-warning sw-btn-sm">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
Edit
|
||||
</a>
|
||||
@if($item->status == 'aktif')
|
||||
<form action="{{ route('admin.siswa.nonaktif', $item->id) }}" method="POST" class="sw-nonaktif-form">
|
||||
@csrf
|
||||
<input type="text" name="alasan" placeholder="Alasan..." required class="sw-nonaktif-input">
|
||||
<button type="submit" class="sw-btn sw-btn-danger sw-btn-sm">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
|
||||
Nonaktif
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<a href="{{ route('admin.siswa.aktifkan', $item->id) }}"
|
||||
class="sw-btn sw-btn-success sw-btn-sm">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
Aktifkan
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="12">
|
||||
<div class="sw-empty">
|
||||
<div class="sw-empty-icon">🎓</div>
|
||||
<p>Tidak ada data siswa ditemukan</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- CHECK ALL --}}
|
||||
<script>
|
||||
document.getElementById('checkAll').addEventListener('click', function() {
|
||||
let checkboxes = document.querySelectorAll('input[name="ids[]"]');
|
||||
checkboxes.forEach(cb => cb.checked = this.checked);
|
||||
document.getElementById('checkAll').addEventListener('change', function () {
|
||||
document.querySelectorAll('input[name="ids[]"]').forEach(cb => cb.checked = this.checked);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,161 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container mt-5">
|
||||
|
||||
<div class="card shadow-sm col-md-6 mx-auto">
|
||||
<div class="card-body">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<h4 class="mb-3 text-center">Ganti Password</h4>
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light:#EEF2FF;
|
||||
--danger: #DC2626;
|
||||
--neutral-light:#F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page {
|
||||
min-height: 100vh; background: var(--bg);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
.sw-card {
|
||||
width: 100%; max-width: 440px;
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,.07);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
padding: 28px 32px 24px;
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
border-bottom: 1px solid #DDD6FE;
|
||||
text-align: center;
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 52px; height: 52px;
|
||||
background: var(--primary);
|
||||
border-radius: 14px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; margin: 0 auto 16px;
|
||||
box-shadow: 0 4px 14px rgba(79,70,229,.3);
|
||||
}
|
||||
.sw-card-header h4 {
|
||||
font-size: 18px; font-weight: 700;
|
||||
color: #3730A3; margin: 0 0 4px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
.sw-card-header p {
|
||||
font-size: 12.5px; color: #6D6AA4; margin: 0;
|
||||
}
|
||||
|
||||
.sw-card-body { padding: 28px 32px; }
|
||||
|
||||
.sw-info-banner {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
background: var(--primary-light);
|
||||
border: 1px solid #C7D2FE;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 12px 14px;
|
||||
font-size: 12.5px; color: #3730A3;
|
||||
margin-bottom: 24px; line-height: 1.6;
|
||||
}
|
||||
.sw-info-banner svg { flex-shrink: 0; margin-top: 1px; }
|
||||
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input-wrap { position: relative; }
|
||||
.sw-input-wrap svg {
|
||||
position: absolute; left: 14px; top: 50%;
|
||||
transform: translateY(-50%); color: #9CA3AF; pointer-events: none;
|
||||
}
|
||||
.sw-input {
|
||||
width: 100%;
|
||||
padding: 11px 14px 11px 42px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-input:focus {
|
||||
border-color: var(--primary);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
.sw-btn-submit {
|
||||
width: 100%; padding: 13px; border-radius: var(--radius-md); border: none;
|
||||
background: var(--primary); color: #fff;
|
||||
font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .18s; margin-top: 8px;
|
||||
box-shadow: 0 4px 14px rgba(79,70,229,.3);
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
}
|
||||
.sw-btn-submit:hover { background: var(--primary-dark); transform: translateY(-1px); box-shadow: 0 6px 20px rgba(79,70,229,.35); }
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
<div class="sw-card">
|
||||
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
</div>
|
||||
<h4>Ganti Password</h4>
|
||||
<p>Buat password baru yang kuat untuk akun Anda</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-body">
|
||||
|
||||
<div class="sw-info-banner">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||||
Anda diwajibkan mengganti password sebelum dapat menggunakan sistem. Gunakan minimal 8 karakter.
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('force.password.update') }}">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Password Baru</label>
|
||||
<input type="password" name="password" class="form-control" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Password Baru <span>*</span></label>
|
||||
<div class="sw-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<input type="password" name="password" class="sw-input" placeholder="Minimal 8 karakter" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Konfirmasi Password</label>
|
||||
<input type="password" name="password_confirmation" class="form-control" required>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Konfirmasi Password <span>*</span></label>
|
||||
<div class="sw-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
<input type="password" name="password_confirmation" class="sw-input" placeholder="Ulangi password" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100">Simpan Password</button>
|
||||
<button type="submit" class="sw-btn-submit">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Simpan Password
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -2,165 +2,177 @@
|
|||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Lupa Password</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lupa Password - Sistem Akademik</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Plus Jakarta Sans', sans-serif; }
|
||||
html, body { height: 100%; }
|
||||
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
.page { display: flex; height: 100vh; width: 100%; }
|
||||
|
||||
/* CONTAINER */
|
||||
.container-login {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
/* ── LEFT ── */
|
||||
.left {
|
||||
width: 38%;
|
||||
background: #F3F4F8;
|
||||
display: flex; flex-direction: column;
|
||||
justify-content: center; align-items: center;
|
||||
text-align: center; padding: 48px 40px;
|
||||
position: relative;
|
||||
}
|
||||
.left::after {
|
||||
content: '';
|
||||
position: absolute; right: 0; top: 0; bottom: 0; width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, #C7D2FE 30%, #C7D2FE 70%, transparent);
|
||||
}
|
||||
|
||||
/* LEFT */
|
||||
.left {
|
||||
width: 40%;
|
||||
background: #f4f6fb;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
.logo {
|
||||
width: 280px;
|
||||
max-width: 100%;
|
||||
margin-bottom: 32px;
|
||||
|
||||
.logo {
|
||||
width: 250px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
filter: drop-shadow(0 8px 24px rgba(79,70,229,.18));
|
||||
transition: transform .3s;
|
||||
}
|
||||
.left-title { font-size: 22px; font-weight: 800; color: #1E3A6E; line-height: 1.3; margin-bottom: 10px; }
|
||||
.left-sub { font-size: 13px; color: #6B7280; line-height: 1.7; }
|
||||
|
||||
.left h2 {
|
||||
color: #2c5aa0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.left-badge {
|
||||
margin-top: 28px;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: #EEF2FF; border: 1px solid #C7D2FE;
|
||||
border-radius: 99px; padding: 6px 16px;
|
||||
font-size: 11.5px; font-weight: 600; color: #4F46E5;
|
||||
}
|
||||
.left-badge span { width: 6px; height: 6px; background: #4F46E5; border-radius: 50%; display: inline-block; }
|
||||
|
||||
.left p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
/* ── RIGHT ── */
|
||||
.right {
|
||||
flex: 1;
|
||||
background: linear-gradient(145deg, #1E3A6E 0%, #2D5BB5 55%, #4F8DD6 100%);
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.right::before {
|
||||
content: ''; position: absolute;
|
||||
width: 420px; height: 420px; border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.07);
|
||||
top: -80px; right: -80px;
|
||||
}
|
||||
.right::after {
|
||||
content: ''; position: absolute;
|
||||
width: 300px; height: 300px; border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.06);
|
||||
bottom: -60px; left: -60px;
|
||||
}
|
||||
|
||||
/* RIGHT */
|
||||
.right {
|
||||
width: 60%;
|
||||
background: linear-gradient(135deg, #3b6cb7, #4f8dd6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
/* ── FORM BOX ── */
|
||||
.form-box { width: 340px; position: relative; z-index: 2; }
|
||||
|
||||
/* FORM */
|
||||
.form-box {
|
||||
width: 320px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
.form-title {
|
||||
font-size: 24px; font-weight: 800; color: #fff;
|
||||
letter-spacing: -.5px; margin-bottom: 6px;
|
||||
}
|
||||
.form-sub {
|
||||
font-size: 13px; color: rgba(255,255,255,.65);
|
||||
line-height: 1.6; margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.form-box h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 5px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
/* ── INPUT ── */
|
||||
.input-wrap { position: relative; margin-bottom: 14px; }
|
||||
.input-wrap svg {
|
||||
position: absolute; left: 16px; top: 50%;
|
||||
transform: translateY(-50%); color: rgba(255,255,255,.5); pointer-events: none;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%;
|
||||
padding: 13px 16px 13px 44px;
|
||||
border-radius: 12px;
|
||||
border: 1.5px solid rgba(255,255,255,.15);
|
||||
background: rgba(255,255,255,.12);
|
||||
color: #fff; font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
outline: none; transition: all .2s;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.input-wrap input::placeholder { color: rgba(255,255,255,.45); }
|
||||
.input-wrap input:focus {
|
||||
border-color: rgba(255,255,255,.5);
|
||||
background: rgba(255,255,255,.18);
|
||||
box-shadow: 0 0 0 3px rgba(255,255,255,.1);
|
||||
}
|
||||
|
||||
.form-box p {
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
/* ── BUTTON ── */
|
||||
.btn-submit {
|
||||
width: 100%; padding: 13px; border-radius: 12px; border: none;
|
||||
background: linear-gradient(135deg, #10B981, #059669);
|
||||
color: #fff; font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .2s; margin-top: 4px;
|
||||
box-shadow: 0 4px 16px rgba(5,150,105,.35);
|
||||
}
|
||||
.btn-submit:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(5,150,105,.4); }
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
margin-bottom: 15px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
background: #00c851;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #00a844;
|
||||
}
|
||||
|
||||
.back {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
/* ── ALERT ── */
|
||||
.sw-alert-success {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
background: rgba(16,185,129,.15); border: 1px solid rgba(16,185,129,.3);
|
||||
border-radius: 10px; padding: 11px 14px;
|
||||
font-size: 12.5px; color: #6EE7B7; margin-bottom: 18px;
|
||||
}
|
||||
|
||||
/* ── BACK LINK ── */
|
||||
.back-link {
|
||||
display: flex; align-items: center; justify-content: center; gap: 6px;
|
||||
margin-top: 20px;
|
||||
font-size: 12.5px; color: rgba(255,255,255,.6);
|
||||
text-decoration: none; transition: color .2s;
|
||||
}
|
||||
.back-link:hover { color: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-login">
|
||||
<div class="page">
|
||||
|
||||
<!-- LEFT -->
|
||||
<div class="left">
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo">
|
||||
|
||||
<h2>SMAN 1 BONDOWOSO</h2>
|
||||
<p>Silakan masukkan email untuk reset password</p>
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo" alt="Logo">
|
||||
<div class="left-title">SMAN 1<br>BONDOWOSO</div>
|
||||
<div class="left-sub">Silakan masukkan email untuk<br>reset password akun Anda</div>
|
||||
<div class="left-badge"><span></span> Sistem Akademik Sekolah</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT -->
|
||||
<div class="right">
|
||||
<div class="form-box">
|
||||
|
||||
<h3>LUPA PASSWORD</h3>
|
||||
<p>Masukkan email untuk menerima kode OTP</p>
|
||||
<div class="form-title">Lupa Password?</div>
|
||||
<div class="form-sub">Masukkan email terdaftar dan kami akan mengirimkan kode OTP untuk reset password.</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
<div class="sw-alert-success">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="/forgot-password">
|
||||
@csrf
|
||||
|
||||
<input type="email" name="email" placeholder="Enter Email" required>
|
||||
|
||||
<button>Kirim OTP</button>
|
||||
|
||||
<a href="/login" class="back">← Kembali ke Login</a>
|
||||
<div class="input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
||||
<input type="email" name="email" placeholder="Enter Email" required>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Kirim OTP</button>
|
||||
</form>
|
||||
|
||||
<a href="/login" class="back-link">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali ke Login
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,275 +1,618 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Sistem Akademik</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Login - Sistem Akademik</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
html, body { height: 100%; }
|
||||
|
||||
/* CONTAINER */
|
||||
.container-login {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
.page {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* LEFT SIDE */
|
||||
.left {
|
||||
width: 40%;
|
||||
background: #f4f6fb;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
/* LEFT */
|
||||
.left {
|
||||
width: 38%;
|
||||
background: #F3F4F8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 48px 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* LOGO */
|
||||
.logo {
|
||||
width: 300px;
|
||||
margin-bottom: 25px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.left::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0; top: 0; bottom: 0;
|
||||
width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, #C7D2FE 30%, #C7D2FE 70%, transparent);
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.logo {
|
||||
width: 280px;
|
||||
max-width: 100%;
|
||||
margin-bottom: 32px;
|
||||
filter: drop-shadow(0 8px 24px rgba(79, 70, 229, .18));
|
||||
transition: transform .3s;
|
||||
}
|
||||
|
||||
/* TEXT */
|
||||
.left h2 {
|
||||
color: #2c5aa0;
|
||||
font-size: 26px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.logo:hover { transform: scale(1.04); }
|
||||
|
||||
.left p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
.left-title {
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
color: #1E3A6E;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 10px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
|
||||
/* RIGHT SIDE */
|
||||
.right {
|
||||
width: 60%;
|
||||
background: linear-gradient(135deg, #3b6cb7, #4f8dd6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.left-sub {
|
||||
font-size: 13px;
|
||||
color: #6B7280;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* LOGIN FORM */
|
||||
.login-form {
|
||||
width: 280px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
.left-badge {
|
||||
margin-top: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #EEF2FF;
|
||||
border: 1px solid #C7D2FE;
|
||||
border-radius: 99px;
|
||||
padding: 6px 16px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
color: #4F46E5;
|
||||
}
|
||||
|
||||
/* TITLE */
|
||||
.login-form h3 {
|
||||
margin-bottom: 5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.left-badge span {
|
||||
width: 6px; height: 6px;
|
||||
background: #4F46E5;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.login-form small {
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
/* RIGHT */
|
||||
.right {
|
||||
flex: 1;
|
||||
background: linear-gradient(145deg, #1E3A6E 0%, #2D5BB5 55%, #4F8DD6 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* INPUT */
|
||||
.login-form input {
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
margin-bottom: 15px;
|
||||
outline: none;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.right::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 420px; height: 420px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.07);
|
||||
top: -80px; right: -80px;
|
||||
}
|
||||
|
||||
.login-form input:focus {
|
||||
box-shadow: 0 0 0 2px rgba(255,255,255,0.5);
|
||||
}
|
||||
.right::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 300px; height: 300px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.06);
|
||||
bottom: -60px; left: -60px;
|
||||
}
|
||||
|
||||
/* BUTTON */
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #00c851, #00a844);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
/* FORM BOX */
|
||||
.form-box {
|
||||
width: 340px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
|
||||
}
|
||||
.form-title {
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
letter-spacing: -.5px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* LINK */
|
||||
.forgot {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.form-sub {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,.6);
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.forgot a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.form-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 18px 0;
|
||||
}
|
||||
|
||||
.forgot a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.form-divider-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,.2);
|
||||
}
|
||||
|
||||
/* ALERT */
|
||||
.alert {
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-divider-text {
|
||||
font-size: 11px;
|
||||
color: rgba(255,255,255,.5);
|
||||
font-weight: 600;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
|
||||
/* TITLE */
|
||||
.login-form h3 {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 5px;
|
||||
color: #ffffff;
|
||||
}
|
||||
.section-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: rgba(255,255,255,.5);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* SUBTITLE */
|
||||
.login-form small {
|
||||
display: block;
|
||||
margin-bottom: 25px;
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.7);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* INPUT */
|
||||
.login-form input {
|
||||
width: 100%;
|
||||
padding: 14px 18px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
margin-bottom: 15px;
|
||||
outline: none;
|
||||
background: rgba(255,255,255,0.9);
|
||||
font-size: 14px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.input-wrap svg {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #9CA3AF;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-form input::placeholder {
|
||||
color: #999;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%;
|
||||
padding: 13px 16px 13px 44px;
|
||||
border-radius: 12px;
|
||||
border: 1.5px solid rgba(255,255,255,.15);
|
||||
background: rgba(255,255,255,.12);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
outline: none;
|
||||
transition: all .2s;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
/* FOCUS EFFECT */
|
||||
.login-form input:focus {
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(255,255,255,0.3);
|
||||
}
|
||||
.input-wrap input::placeholder { color: rgba(255,255,255,.45); }
|
||||
|
||||
/* BUTTON */
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #00c851, #00a844);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.input-wrap input:focus {
|
||||
border-color: rgba(255,255,255,.5);
|
||||
background: rgba(255,255,255,.18);
|
||||
box-shadow: 0 0 0 3px rgba(255,255,255,.1);
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.25);
|
||||
}
|
||||
</style>
|
||||
.btn-otp {
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
border-radius: 12px;
|
||||
border: 1.5px solid rgba(255,255,255,.35);
|
||||
background: rgba(255,255,255,.1);
|
||||
color: #fff;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.btn-otp:hover {
|
||||
background: rgba(255,255,255,.2);
|
||||
border-color: rgba(255,255,255,.5);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 13px;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #10B981, #059669);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
margin-top: 4px;
|
||||
box-shadow: 0 4px 16px rgba(5,150,105,.35);
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(5,150,105,.4);
|
||||
}
|
||||
|
||||
/* Alert error */
|
||||
.sw-alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(239,68,68,.15);
|
||||
border: 1px solid rgba(239,68,68,.3);
|
||||
border-radius: 10px;
|
||||
padding: 10px 14px;
|
||||
font-size: 12.5px;
|
||||
color: #FCA5A5;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Alert success / OTP sent */
|
||||
.sw-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: rgba(16,185,129,.15);
|
||||
border: 1px solid rgba(16,185,129,.35);
|
||||
border-radius: 10px;
|
||||
padding: 10px 14px;
|
||||
font-size: 12.5px;
|
||||
color: #6EE7B7;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.forgot-link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
font-size: 12.5px;
|
||||
color: rgba(255,255,255,.6);
|
||||
text-decoration: none;
|
||||
transition: color .2s;
|
||||
}
|
||||
|
||||
.forgot-link:hover { color: #fff; }
|
||||
|
||||
.method-btn {
|
||||
flex: 1;
|
||||
padding: 9px 10px;
|
||||
border-radius: 10px;
|
||||
border: 1.5px solid rgba(255,255,255,.2);
|
||||
background: rgba(255,255,255,.08);
|
||||
color: rgba(255,255,255,.7);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.method-btn:hover {
|
||||
background: rgba(255,255,255,.15);
|
||||
border-color: rgba(255,255,255,.4);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.method-btn.active {
|
||||
background: rgba(255,255,255,.22);
|
||||
border-color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Admin info badge */
|
||||
.admin-info-badge {
|
||||
display: none;
|
||||
font-size: 11.5px;
|
||||
color: rgba(255,255,255,.75);
|
||||
background: rgba(255,255,255,.08);
|
||||
border: 1px solid rgba(255,255,255,.18);
|
||||
border-radius: 10px;
|
||||
padding: 9px 13px;
|
||||
margin-bottom: 12px;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.admin-info-badge svg {
|
||||
flex-shrink: 0;
|
||||
color: #6EE7B7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container-login">
|
||||
<div class="page">
|
||||
|
||||
<!-- LEFT -->
|
||||
<div class="left">
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo">
|
||||
|
||||
<h2>SMAN 1<br>BONDOWOSO</h2>
|
||||
<p>Masukkan email dan password<br>untuk melanjutkan</p>
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo" alt="Logo">
|
||||
<div class="left-title">SMAN 1<br>BONDOWOSO</div>
|
||||
<div class="left-sub">Masukkan email dan password<br>untuk melanjutkan ke portal</div>
|
||||
<div class="left-badge">
|
||||
<span></span> Sistem Akademik Sekolah
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT -->
|
||||
<div class="right">
|
||||
<div class="login-form">
|
||||
<!-- RIGHT -->
|
||||
<div class="right">
|
||||
<div class="form-box">
|
||||
|
||||
<h3>SIGN IN</h3>
|
||||
<small>TO ACCESS THE PORTAL</small>
|
||||
<div class="form-title">SIGN IN</div>
|
||||
<div class="form-sub">TO ACCESS THE PORTAL</div>
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert">
|
||||
{{ session('error') }}
|
||||
{{-- Alert error --}}
|
||||
@if(session('error'))
|
||||
<div class="sw-alert">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="8" x2="12" y2="12"/>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Alert OTP berhasil dikirim --}}
|
||||
@if(session('otp_sent'))
|
||||
<div class="sw-success">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
{{ session('otp_sent') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- ============================================================
|
||||
LANGKAH 1: Dapatkan OTP
|
||||
Langkah ini disembunyikan otomatis jika email adalah admin
|
||||
============================================================ --}}
|
||||
<div id="step1-section">
|
||||
<div class="section-label">Langkah 1 — Dapatkan OTP</div>
|
||||
|
||||
<div style="font-size:10px; font-weight:700; color:rgba(255,255,255,.5); text-transform:uppercase; letter-spacing:1px; margin-bottom:8px;">
|
||||
Pilih metode pengiriman OTP
|
||||
</div>
|
||||
|
||||
<div style="display:flex; gap:8px; margin-bottom:10px;">
|
||||
<button type="button" class="method-btn active" id="btn-email-method" onclick="selectMethod('email')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
Email
|
||||
</button>
|
||||
<button type="button" class="method-btn" id="btn-wa-method" onclick="selectMethod('wa')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
|
||||
</svg>
|
||||
WhatsApp
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('send.otp') }}" id="form-otp">
|
||||
@csrf
|
||||
<input type="hidden" name="method" id="otp_method" value="email">
|
||||
|
||||
{{-- Input email OTP --}}
|
||||
<div class="input-wrap" id="wrap-email-otp">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
<input type="email" name="email" id="input-email-otp" placeholder="Email untuk OTP" required>
|
||||
</div>
|
||||
|
||||
{{-- Input nomor WA --}}
|
||||
<div class="input-wrap" id="wrap-wa-otp" style="display:none;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2A19.79 19.79 0 0 1 3.08 4.18 2 2 0 0 1 5.09 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L9.91 9.91a16 16 0 0 0 6.18 6.18l1.27-1.34a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>
|
||||
</svg>
|
||||
<input type="text" name="telepon" id="input-wa-otp" placeholder="Nomor WhatsApp (08xx...)">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-otp">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>
|
||||
<span id="btn-otp-label">Dapatkan OTP via Email</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="form-divider">
|
||||
<div class="form-divider-line"></div>
|
||||
<div class="form-divider-text">Langkah 2 — Login</div>
|
||||
<div class="form-divider-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- FORM KIRIM OTP --}}
|
||||
<form method="POST" action="{{ route('send.otp') }}">
|
||||
@csrf
|
||||
{{-- Divider khusus admin (tampil menggantikan step1 + divider di atas) --}}
|
||||
<div id="admin-divider" style="display:none;">
|
||||
<div class="form-divider" style="margin-bottom:16px;">
|
||||
<div class="form-divider-line"></div>
|
||||
<div class="form-divider-text">Login Admin</div>
|
||||
<div class="form-divider-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="email" name="email" placeholder="Email untuk OTP" required>
|
||||
{{-- ============================================================
|
||||
LANGKAH 2: Login
|
||||
Field OTP disembunyikan otomatis untuk admin
|
||||
============================================================ --}}
|
||||
<form method="POST" action="{{ route('login.otp') }}">
|
||||
@csrf
|
||||
|
||||
<button type="submit" style="margin-bottom:10px;">
|
||||
🔑 Dapatkan OTP
|
||||
</button>
|
||||
</form>
|
||||
{{-- Email --}}
|
||||
<div class="input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="login-email"
|
||||
placeholder="Email"
|
||||
value="{{ session('email') ?? old('email') }}"
|
||||
oninput="checkAdminEmail(this.value)"
|
||||
required
|
||||
autocomplete="email"
|
||||
>
|
||||
</div>
|
||||
|
||||
{{-- Password --}}
|
||||
<div class="input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>
|
||||
<input type="password" name="password" placeholder="Password" required autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
{{-- FORM LOGIN --}}
|
||||
<form method="POST" action="{{ route('login.otp') }}">
|
||||
@csrf
|
||||
{{-- OTP — disembunyikan untuk admin --}}
|
||||
<div class="input-wrap" id="wrap-otp-field">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="9 11 12 14 22 4"/>
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
name="otp"
|
||||
id="otp-input"
|
||||
placeholder="Masukkan OTP"
|
||||
maxlength="6"
|
||||
inputmode="numeric"
|
||||
autocomplete="one-time-code"
|
||||
>
|
||||
</div>
|
||||
|
||||
<input type="email" name="email" placeholder="Email" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<input type="text" name="otp" placeholder="Masukkan OTP">
|
||||
{{-- Badge info admin --}}
|
||||
<div class="admin-info-badge" id="admin-info-badge">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
</svg>
|
||||
Akun admin tidak memerlukan kode OTP
|
||||
</div>
|
||||
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<div style="margin-top:10px;">
|
||||
<a href="{{ route('password.request') }}" style="font-size:14px; color:#fff;">
|
||||
Lupa Password?
|
||||
</a>
|
||||
</div>
|
||||
<button type="submit" class="btn-login">Masuk ke Portal</button>
|
||||
</form>
|
||||
|
||||
<a href="{{ route('password.request') }}" class="forgot-link">Lupa Password?</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ================================================================
|
||||
// Daftar email admin — sesuaikan dengan data di database Anda
|
||||
// ================================================================
|
||||
// const ADMIN_EMAILS = [
|
||||
// 'admin@sman1bondowoso.sch.id',
|
||||
// // tambahkan email admin lain di sini jika ada
|
||||
// ];
|
||||
|
||||
// ================================================================
|
||||
// Toggle tampilan Langkah 1 (OTP section) berdasarkan role
|
||||
// ================================================================
|
||||
function checkAdminEmail(email) {
|
||||
const isAdmin = ADMIN_EMAILS.includes(email.trim().toLowerCase());
|
||||
|
||||
const step1 = document.getElementById('step1-section');
|
||||
const adminDiv = document.getElementById('admin-divider');
|
||||
const otpWrap = document.getElementById('wrap-otp-field');
|
||||
const otpInput = document.getElementById('otp-input');
|
||||
const adminBadge = document.getElementById('admin-info-badge');
|
||||
|
||||
if (isAdmin) {
|
||||
// Sembunyikan Langkah 1 dan field OTP
|
||||
step1.style.display = 'none';
|
||||
adminDiv.style.display = 'block';
|
||||
otpWrap.style.display = 'none';
|
||||
adminBadge.style.display = 'flex';
|
||||
otpInput.required = false;
|
||||
otpInput.value = '';
|
||||
} else {
|
||||
// Tampilkan kembali semua untuk guru/siswa
|
||||
step1.style.display = 'block';
|
||||
adminDiv.style.display = 'none';
|
||||
otpWrap.style.display = 'block';
|
||||
adminBadge.style.display = 'none';
|
||||
otpInput.required = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Toggle metode OTP (email / WhatsApp)
|
||||
// ================================================================
|
||||
function selectMethod(method) {
|
||||
const emailBtn = document.getElementById('btn-email-method');
|
||||
const waBtn = document.getElementById('btn-wa-method');
|
||||
const emailWrap = document.getElementById('wrap-email-otp');
|
||||
const waWrap = document.getElementById('wrap-wa-otp');
|
||||
const emailInput = document.getElementById('input-email-otp');
|
||||
const waInput = document.getElementById('input-wa-otp');
|
||||
const label = document.getElementById('btn-otp-label');
|
||||
const methodInput = document.getElementById('otp_method');
|
||||
|
||||
if (method === 'email') {
|
||||
emailBtn.classList.add('active');
|
||||
waBtn.classList.remove('active');
|
||||
emailWrap.style.display = 'block';
|
||||
waWrap.style.display = 'none';
|
||||
emailInput.required = true;
|
||||
waInput.required = false;
|
||||
waInput.value = '';
|
||||
label.textContent = 'Dapatkan OTP via Email';
|
||||
methodInput.value = 'email';
|
||||
} else {
|
||||
waBtn.classList.add('active');
|
||||
emailBtn.classList.remove('active');
|
||||
waWrap.style.display = 'block';
|
||||
emailWrap.style.display = 'none';
|
||||
waInput.required = true;
|
||||
emailInput.required = false;
|
||||
emailInput.value = '';
|
||||
label.textContent = 'Dapatkan OTP via WhatsApp';
|
||||
methodInput.value = 'wa';
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Jalankan saat halaman load — untuk prefill dari session (via WA)
|
||||
// ================================================================
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const emailEl = document.getElementById('login-email');
|
||||
if (emailEl && emailEl.value.trim() !== '') {
|
||||
checkAdminEmail(emailEl.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,35 +1,173 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container mt-5">
|
||||
<div class="card shadow p-4">
|
||||
<h4 class="mb-3 text-center">Reset Password</h4>
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
{{-- SUCCESS --}}
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
:root {
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
|
||||
<form method="POST" action="{{ route('password.update') }}">
|
||||
@csrf
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<input type="hidden" name="email" value="{{ request('email') }}">
|
||||
.sw-page {
|
||||
min-height: 100vh; background: var(--bg);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
<div>
|
||||
<input type="password" name="password" placeholder="Password baru" required>
|
||||
.sw-card {
|
||||
width: 100%; max-width: 440px;
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,.07);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sw-card-header {
|
||||
padding: 28px 32px 24px;
|
||||
background: linear-gradient(135deg, #FFFBEB 0%, #FEF3C7 100%);
|
||||
border-bottom: 1px solid #FDE68A;
|
||||
text-align: center;
|
||||
}
|
||||
.sw-card-header-icon {
|
||||
width: 52px; height: 52px;
|
||||
background: var(--warning);
|
||||
border-radius: 14px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; margin: 0 auto 16px;
|
||||
box-shadow: 0 4px 14px rgba(217,119,6,.3);
|
||||
}
|
||||
.sw-card-header h4 {
|
||||
font-size: 18px; font-weight: 700;
|
||||
color: #92400E; margin: 0 0 4px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
.sw-card-header p { font-size: 12.5px; color: #A16207; margin: 0; }
|
||||
|
||||
.sw-card-body { padding: 28px 32px; }
|
||||
|
||||
/* ALERTS */
|
||||
.sw-alert {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 12px 14px;
|
||||
font-size: 12.5px; margin-bottom: 20px;
|
||||
}
|
||||
.sw-alert-danger { background: var(--danger-light); border: 1px solid #FECACA; color: var(--danger); }
|
||||
.sw-alert-success { background: var(--success-light); border: 1px solid #A7F3D0; color: var(--success); }
|
||||
|
||||
/* FORM */
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input-wrap { position: relative; }
|
||||
.sw-input-wrap svg {
|
||||
position: absolute; left: 14px; top: 50%;
|
||||
transform: translateY(-50%); color: #9CA3AF; pointer-events: none;
|
||||
}
|
||||
.sw-input {
|
||||
width: 100%;
|
||||
padding: 11px 14px 11px 42px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-input:focus {
|
||||
border-color: var(--warning);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(217,119,6,.1);
|
||||
}
|
||||
.sw-input::placeholder { color: #C3C7D0; }
|
||||
|
||||
.sw-btn-submit {
|
||||
width: 100%; padding: 13px; border-radius: var(--radius-md); border: none;
|
||||
background: var(--warning); color: #fff;
|
||||
font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .18s; margin-top: 8px;
|
||||
box-shadow: 0 4px 14px rgba(217,119,6,.3);
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
}
|
||||
.sw-btn-submit:hover { background: #B45309; transform: translateY(-1px); box-shadow: 0 6px 20px rgba(217,119,6,.35); }
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
<div class="sw-card">
|
||||
|
||||
<div class="sw-card-header">
|
||||
<div class="sw-card-header-icon">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
|
||||
</div>
|
||||
<h4>Reset Password</h4>
|
||||
<p>Buat password baru untuk akun Anda</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="password" name="password_confirmation" placeholder="Konfirmasi password" required>
|
||||
<div class="sw-card-body">
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if(session('error'))
|
||||
<div class="sw-alert sw-alert-danger">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<button type="submit">Reset Password</button>
|
||||
</form>
|
||||
{{-- SUCCESS --}}
|
||||
@if(session('success'))
|
||||
<div class="sw-alert sw-alert-success">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('password.update') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="email" value="{{ request('email') }}">
|
||||
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Password Baru <span>*</span></label>
|
||||
<div class="sw-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<input type="password" name="password" class="sw-input" placeholder="Password baru" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Konfirmasi Password <span>*</span></label>
|
||||
<div class="sw-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
<input type="password" name="password_confirmation" class="sw-input" placeholder="Konfirmasi password" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="sw-btn-submit">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Reset Password
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -2,161 +2,267 @@
|
|||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Verifikasi OTP</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verifikasi OTP - Sistem Akademik</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Plus Jakarta Sans', sans-serif; }
|
||||
html, body { height: 100%; }
|
||||
|
||||
body {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
.page { display: flex; height: 100vh; width: 100%; }
|
||||
|
||||
.container-login {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
/* ── LEFT ── */
|
||||
.left {
|
||||
width: 38%;
|
||||
background: #F3F4F8;
|
||||
display: flex; flex-direction: column;
|
||||
justify-content: center; align-items: center;
|
||||
text-align: center; padding: 48px 40px;
|
||||
position: relative;
|
||||
}
|
||||
.left::after {
|
||||
content: '';
|
||||
position: absolute; right: 0; top: 0; bottom: 0; width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, #C7D2FE 30%, #C7D2FE 70%, transparent);
|
||||
}
|
||||
|
||||
/* LEFT */
|
||||
.left {
|
||||
width: 40%;
|
||||
background: #f4f6fb;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
.logo {
|
||||
width: 280px;
|
||||
max-width: 100%;
|
||||
margin-bottom: 32px;
|
||||
|
||||
.logo {
|
||||
width: 250px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
filter: drop-shadow(0 8px 24px rgba(79,70,229,.18));
|
||||
transition: transform .3s;
|
||||
}
|
||||
.left-title { font-size: 22px; font-weight: 800; color: #1E3A6E; line-height: 1.3; margin-bottom: 10px; }
|
||||
.left-sub { font-size: 13px; color: #6B7280; line-height: 1.7; }
|
||||
|
||||
.left h2 {
|
||||
color: #2c5aa0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.left-badge {
|
||||
margin-top: 28px;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: #EEF2FF; border: 1px solid #C7D2FE;
|
||||
border-radius: 99px; padding: 6px 16px;
|
||||
font-size: 11.5px; font-weight: 600; color: #4F46E5;
|
||||
}
|
||||
.left-badge span { width: 6px; height: 6px; background: #4F46E5; border-radius: 50%; display: inline-block; }
|
||||
|
||||
.left p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
/* step indicators */
|
||||
.steps {
|
||||
margin-top: 36px;
|
||||
display: flex; flex-direction: column; gap: 12px;
|
||||
width: 100%; max-width: 220px;
|
||||
}
|
||||
.step {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
.step-dot {
|
||||
width: 30px; height: 30px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; font-weight: 700; flex-shrink: 0;
|
||||
}
|
||||
.step-dot.done { background: #D1FAE5; color: #059669; }
|
||||
.step-dot.active { background: #4F46E5; color: #fff; box-shadow: 0 0 0 4px #C7D2FE; }
|
||||
.step-dot.pending { background: #F3F4F6; color: #9CA3AF; }
|
||||
.step-text { font-size: 12.5px; font-weight: 600; }
|
||||
.step-text.done { color: #059669; }
|
||||
.step-text.active { color: #4F46E5; }
|
||||
.step-text.pending{ color: #9CA3AF; }
|
||||
|
||||
/* RIGHT */
|
||||
.right {
|
||||
width: 60%;
|
||||
background: linear-gradient(135deg, #3b6cb7, #4f8dd6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
/* ── RIGHT ── */
|
||||
.right {
|
||||
flex: 1;
|
||||
background: linear-gradient(145deg, #1E3A6E 0%, #2D5BB5 55%, #4F8DD6 100%);
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.right::before {
|
||||
content: ''; position: absolute;
|
||||
width: 420px; height: 420px; border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.07);
|
||||
top: -80px; right: -80px;
|
||||
}
|
||||
.right::after {
|
||||
content: ''; position: absolute;
|
||||
width: 300px; height: 300px; border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.06);
|
||||
bottom: -60px; left: -60px;
|
||||
}
|
||||
|
||||
/* FORM */
|
||||
.form-box {
|
||||
width: 320px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
/* ── FORM BOX ── */
|
||||
.form-box {
|
||||
width: 340px;
|
||||
position: relative; z-index: 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-box h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
/* OTP icon ring */
|
||||
.otp-icon {
|
||||
width: 68px; height: 68px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.12);
|
||||
border: 2px solid rgba(255,255,255,.25);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 22px;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.form-box p {
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-title {
|
||||
font-size: 24px; font-weight: 800;
|
||||
color: #fff; letter-spacing: -.5px; margin-bottom: 6px;
|
||||
}
|
||||
.form-sub {
|
||||
font-size: 13px; color: rgba(255,255,255,.65);
|
||||
line-height: 1.6; margin-bottom: 32px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
margin-bottom: 15px;
|
||||
outline: none;
|
||||
}
|
||||
/* ── OTP INPUT ── */
|
||||
.input-wrap { position: relative; margin-bottom: 16px; }
|
||||
.input-wrap svg {
|
||||
position: absolute; left: 16px; top: 50%;
|
||||
transform: translateY(-50%); color: rgba(255,255,255,.5); pointer-events: none;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%;
|
||||
padding: 15px 16px 15px 48px;
|
||||
border-radius: 14px;
|
||||
border: 1.5px solid rgba(255,255,255,.2);
|
||||
background: rgba(255,255,255,.12);
|
||||
color: #fff; font-size: 20px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
letter-spacing: 6px; text-align: center;
|
||||
outline: none; transition: all .2s;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.input-wrap input::placeholder {
|
||||
color: rgba(255,255,255,.3);
|
||||
font-size: 14px; font-weight: 400; letter-spacing: 1px;
|
||||
}
|
||||
.input-wrap input:focus {
|
||||
border-color: rgba(255,255,255,.55);
|
||||
background: rgba(255,255,255,.2);
|
||||
box-shadow: 0 0 0 3px rgba(255,255,255,.12);
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
background: #00c851;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* expiry hint */
|
||||
.otp-hint {
|
||||
font-size: 12px; color: rgba(255,255,255,.5);
|
||||
margin-bottom: 20px; display: flex; align-items: center;
|
||||
justify-content: center; gap: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #00a844;
|
||||
}
|
||||
/* ── BUTTON ── */
|
||||
.btn-verify {
|
||||
width: 100%; padding: 14px; border-radius: 12px; border: none;
|
||||
background: linear-gradient(135deg, #10B981, #059669);
|
||||
color: #fff; font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .2s;
|
||||
box-shadow: 0 4px 16px rgba(5,150,105,.4);
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
}
|
||||
.btn-verify:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(5,150,105,.45); }
|
||||
|
||||
.back {
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
}
|
||||
/* ── ALERT ── */
|
||||
.sw-alert {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
background: rgba(239,68,68,.15); border: 1px solid rgba(239,68,68,.3);
|
||||
border-radius: 10px; padding: 11px 14px;
|
||||
font-size: 12.5px; color: #FCA5A5; margin-bottom: 18px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.alert {
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
/* ── BACK LINK ── */
|
||||
.back-link {
|
||||
display: flex; align-items: center; justify-content: center; gap: 6px;
|
||||
margin-top: 20px;
|
||||
font-size: 12.5px; color: rgba(255,255,255,.55);
|
||||
text-decoration: none; transition: color .2s;
|
||||
}
|
||||
.back-link:hover { color: #fff; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.left { display: none; }
|
||||
.right { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container-login">
|
||||
<div class="page">
|
||||
|
||||
<!-- LEFT -->
|
||||
<div class="left">
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo">
|
||||
<img src="{{ asset('sbadmin2/img/logosmasa.png') }}" class="logo" alt="Logo">
|
||||
<div class="left-title">SMAN 1<br>BONDOWOSO</div>
|
||||
<div class="left-sub">Masukkan kode OTP yang telah<br>dikirim ke email Anda</div>
|
||||
<div class="left-badge"><span></span> Verifikasi 2 Langkah</div>
|
||||
|
||||
<h2>SMAN 1 BONDOWOSO</h2>
|
||||
<p>Masukkan kode OTP yang telah dikirim ke email</p>
|
||||
<!-- Step indicators -->
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<div class="step-dot done">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</div>
|
||||
<div class="step-text done">Email & Password</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-dot active">2</div>
|
||||
<div class="step-text active">Verifikasi OTP</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-dot pending">3</div>
|
||||
<div class="step-text pending">Akses Portal</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT -->
|
||||
<div class="right">
|
||||
<div class="form-box">
|
||||
|
||||
<h3>VERIFIKASI OTP</h3>
|
||||
<p>Masukkan kode OTP</p>
|
||||
<div class="otp-icon">
|
||||
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.9)" stroke-width="1.8"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>
|
||||
</div>
|
||||
|
||||
<div class="form-title">Verifikasi OTP</div>
|
||||
<div class="form-sub">Kode OTP telah dikirim ke email Anda.<br>Segera masukkan sebelum kadaluarsa.</div>
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
<div class="sw-alert">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('verify.otp') }}">
|
||||
@csrf
|
||||
|
||||
<input type="text" name="otp" placeholder="Masukkan OTP" required>
|
||||
<div class="input-wrap">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>
|
||||
<input type="text" name="otp" placeholder="_ _ _ _ _ _"
|
||||
maxlength="6" inputmode="numeric" autocomplete="one-time-code" required>
|
||||
</div>
|
||||
|
||||
<button>Verifikasi</button>
|
||||
<div class="otp-hint">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
Kode berlaku selama beberapa menit
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-verify">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
Verifikasi Sekarang
|
||||
</button>
|
||||
|
||||
<a href="/login" class="back">← Kembali ke Login</a>
|
||||
</form>
|
||||
|
||||
<a href="/login" class="back-link">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali ke Login
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<footer class="sticky-footer bg-white">
|
||||
<!-- <footer class="sticky-footer bg-white">
|
||||
<div class="container my-auto">
|
||||
<div class="copyright text-center my-auto">
|
||||
<span>Copyright © Your Website 2020</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</footer> -->
|
||||
|
|
@ -1,225 +1,456 @@
|
|||
{{-- ================================================================
|
||||
FILE: resources/views/layouts/sidebar.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
@php
|
||||
$role = auth()->user()->role ?? null;
|
||||
@endphp
|
||||
|
||||
{{-- ================= ADMIN (SIDEBAR) ================= --}}
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
/* ── SIDEBAR SHELL ─────────────────────────────────── */
|
||||
.custom-sidebar {
|
||||
width: 240px;
|
||||
min-height: 100vh;
|
||||
background: #0f1e3d;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255,255,255,0.08) transparent;
|
||||
}
|
||||
|
||||
.custom-sidebar::-webkit-scrollbar { width: 4px; }
|
||||
.custom-sidebar::-webkit-scrollbar-track { background: transparent; }
|
||||
.custom-sidebar::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }
|
||||
|
||||
/* ── BRAND ─────────────────────────────────────────── */
|
||||
.sb-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
padding: 20px 20px 18px;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sb-brand-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 9px;
|
||||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 4px 12px rgba(37,99,235,0.4);
|
||||
}
|
||||
|
||||
.sb-brand-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sb-brand-name {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.sb-brand-sub {
|
||||
font-size: 10.5px;
|
||||
font-weight: 500;
|
||||
color: rgba(255,255,255,0.35);
|
||||
letter-spacing: 0.05em;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ── SECTION HEADING ───────────────────────────────── */
|
||||
.sb-section {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.28);
|
||||
padding: 14px 20px 6px;
|
||||
}
|
||||
|
||||
/* ── NAV ITEM ──────────────────────────────────────── */
|
||||
.sb-nav {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 10px 16px;
|
||||
}
|
||||
|
||||
.sb-nav li { margin-bottom: 2px; }
|
||||
|
||||
.sb-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
padding: 9px 12px;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
color: rgba(255,255,255,0.55);
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sb-link:hover {
|
||||
background: rgba(255,255,255,0.06);
|
||||
color: rgba(255,255,255,0.9);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sb-link .sb-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
color: rgba(255,255,255,0.45);
|
||||
flex-shrink: 0;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.sb-link:hover .sb-icon {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: rgba(255,255,255,0.85);
|
||||
}
|
||||
|
||||
/* ── ACTIVE STATE ──────────────────────────────────── */
|
||||
.sb-link.active {
|
||||
background: linear-gradient(135deg, rgba(37,99,235,0.35) 0%, rgba(29,78,216,0.2) 100%);
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sb-link.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px; height: 60%;
|
||||
background: #3b82f6;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
.sb-link.active .sb-icon {
|
||||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 3px 8px rgba(37,99,235,0.4);
|
||||
}
|
||||
|
||||
/* ── DIVIDER ───────────────────────────────────────── */
|
||||
.sb-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
margin: 6px 20px 10px;
|
||||
}
|
||||
|
||||
/* ── ROLE BADGE (bottom) ───────────────────────────── */
|
||||
.sb-footer {
|
||||
margin-top: auto;
|
||||
padding: 14px 20px 20px;
|
||||
border-top: 1px solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.sb-role-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.sb-role-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #2563eb, #6366f1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.sb-role-info { overflow: hidden; }
|
||||
|
||||
.sb-role-name {
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
color: rgba(255,255,255,0.85);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.sb-role-label {
|
||||
font-size: 10.5px;
|
||||
color: rgba(255,255,255,0.3);
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
{{-- ================= ADMIN SIDEBAR ================= --}}
|
||||
@if($role == 'admin')
|
||||
|
||||
<ul class="navbar-nav bg-gradient-info sidebar sidebar-dark accordion" id="accordionSidebar">
|
||||
<aside class="custom-sidebar">
|
||||
|
||||
<!-- BRAND -->
|
||||
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="#">
|
||||
<div class="sidebar-brand-icon rotate-n-15">
|
||||
<i class="fas fa-school"></i>
|
||||
<a class="sb-brand" href="{{ route('admin.dashboard') }}">
|
||||
<div class="sb-brand-icon"><i class="fa-solid fa-school"></i></div>
|
||||
<div class="sb-brand-text">
|
||||
<span class="sb-brand-name">SMAN 1</span>
|
||||
<span class="sb-brand-sub">Panel Admin</span>
|
||||
</div>
|
||||
<div class="sidebar-brand-text mx-3">SMAN 1</div>
|
||||
</a>
|
||||
|
||||
<hr class="sidebar-divider my-0">
|
||||
<span class="sb-section">Menu Utama</span>
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-tachometer-alt"></i></span>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('admin.dashboard') }}">
|
||||
<i class="fas fa-fw fa-tachometer-alt"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<div class="sb-divider"></div>
|
||||
<span class="sb-section">Manajemen Data</span>
|
||||
|
||||
<hr class="sidebar-divider">
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('admin.guru.index') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.guru.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-chalkboard-teacher"></i></span>
|
||||
Data Guru
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.siswa.index') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.siswa.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-user-graduate"></i></span>
|
||||
Data Siswa
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.mapel.index') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.mapel.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-book"></i></span>
|
||||
Mata Pelajaran
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.kelas.index') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.kelas.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-school"></i></span>
|
||||
Data Kelas
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('admin.jadwal.index') }}"
|
||||
class="sb-link {{ request()->routeIs('admin.jadwal.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-calendar-alt"></i></span>
|
||||
Jadwal
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="sidebar-heading">Admin Menu</div>
|
||||
<div class="sb-footer">
|
||||
<div class="sb-role-card">
|
||||
<div class="sb-role-avatar">
|
||||
{{ strtoupper(substr(auth()->user()->name, 0, 2)) }}
|
||||
</div>
|
||||
<div class="sb-role-info">
|
||||
<div class="sb-role-name">{{ auth()->user()->name }}</div>
|
||||
<div class="sb-role-label">Administrator</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.guru.index') }}" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>Data Guru</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.siswa.index') }}" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>Data Siswa</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.mapel.index') }}" class="nav-link">
|
||||
<i class="fas fa-book"></i>
|
||||
<span>Mata Pelajaran</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.kelas.index') }}" class="nav-link">
|
||||
<i class="fas fa-school"></i>
|
||||
<span>Data Kelas</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.jadwal.index') }}" class="nav-link">
|
||||
<i class="fas fa-calendar"></i>
|
||||
<span>Jadwal</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
{{-- ================= GURU (NAVBAR MOBILE) ================= --}}
|
||||
{{-- ================= GURU SIDEBAR ================= --}}
|
||||
@if(auth()->check() && auth()->user()->role == 'guru')
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-info shadow-sm fixed-top">
|
||||
<div class="container-fluid">
|
||||
<aside class="custom-sidebar">
|
||||
|
||||
<!-- Brand -->
|
||||
<a class="navbar-brand fw-bold" href="{{ route('guru.dashboard') }}">
|
||||
<i class="fas fa-school"></i> SMAN 1
|
||||
</a>
|
||||
<a class="sb-brand" href="{{ route('guru.dashboard') }}">
|
||||
<div class="sb-brand-icon"><i class="fa-solid fa-school"></i></div>
|
||||
<div class="sb-brand-text">
|
||||
<span class="sb-brand-name">SMAN 1</span>
|
||||
<span class="sb-brand-sub">Portal Guru</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Toggle -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarGuru">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<span class="sb-section">Menu Utama</span>
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('guru.dashboard') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.dashboard') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-tachometer-alt"></i></span>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="collapse navbar-collapse" id="navbarGuru">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<div class="sb-divider"></div>
|
||||
<span class="sb-section">Menu Guru</span>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('guru.dashboard') ? 'active' : '' }}"
|
||||
href="{{ route('guru.dashboard') }}">
|
||||
<i class="fas fa-home"></i> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('guru.jadwal') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.jadwal') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-calendar-alt"></i></span>
|
||||
Jadwal
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('guru.absensi') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.absensi*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-user-check"></i></span>
|
||||
Absensi
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('guru.nilai.index') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.nilai.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-clipboard-list"></i></span>
|
||||
Nilai
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('guru.tugas.index') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.tugas.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-tasks"></i></span>
|
||||
Tugas
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('guru.ujian.index') }}"
|
||||
class="sb-link {{ request()->routeIs('guru.ujian.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-file-alt"></i></span>
|
||||
Ujian
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('guru.jadwal') ? 'active' : '' }}"
|
||||
href="{{ route('guru.jadwal') }}">
|
||||
<i class="fas fa-calendar"></i> Jadwal
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('guru.absensi*') ? 'active' : '' }}"
|
||||
href="{{ route('guru.absensi') }}">
|
||||
<i class="fas fa-user-check"></i> Absensi
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('guru.nilai.*') ? 'active' : '' }}"
|
||||
href="{{ route('guru.nilai.index') }}">
|
||||
<i class="fas fa-clipboard-list"></i> Nilai
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{{-- 🔥 MENU BARU: TUGAS --}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('guru.tugas.*') ? 'active' : '' }}"
|
||||
href="{{ route('guru.tugas.index') }}">
|
||||
<i class="fas fa-tasks"></i> Tugas
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('guru.ujian.index') }}" class="nav-link">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<p>Ujian</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="sb-footer">
|
||||
<div class="sb-role-card">
|
||||
<div class="sb-role-avatar">
|
||||
{{ strtoupper(substr(auth()->user()->name, 0, 2)) }}
|
||||
</div>
|
||||
<div class="sb-role-info">
|
||||
<div class="sb-role-name">{{ auth()->user()->name }}</div>
|
||||
<div class="sb-role-label">Guru</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{-- supaya konten tidak ketutup navbar --}}
|
||||
<style>
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
</style>
|
||||
</aside>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
{{-- ================= SISWA ================= --}}
|
||||
{{-- ================= SISWA SIDEBAR ================= --}}
|
||||
@if($role == 'siswa')
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-info shadow-sm fixed-top">
|
||||
<div class="container-fluid">
|
||||
<aside class="custom-sidebar">
|
||||
|
||||
<!-- Brand -->
|
||||
<a class="navbar-brand fw-bold" href="#">
|
||||
<i class="fas fa-school"></i> SMAN 1
|
||||
</a>
|
||||
<a class="sb-brand" href="{{ route('siswa.dashboard') }}">
|
||||
<div class="sb-brand-icon"><i class="fa-solid fa-school"></i></div>
|
||||
<div class="sb-brand-text">
|
||||
<span class="sb-brand-name">SMAN 1</span>
|
||||
<span class="sb-brand-sub">Portal Siswa</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Toggle -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSiswa">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<span class="sb-section">Menu Utama</span>
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('siswa.dashboard') }}"
|
||||
class="sb-link {{ request()->routeIs('siswa.dashboard') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-tachometer-alt"></i></span>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="collapse navbar-collapse" id="navbarSiswa">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<div class="sb-divider"></div>
|
||||
<span class="sb-section">Menu Siswa</span>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('siswa/dashboard') ? 'active' : '' }}"
|
||||
href="{{ route('siswa.dashboard') }}">
|
||||
<i class="fas fa-home"></i> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<ul class="sb-nav">
|
||||
<li>
|
||||
<a href="{{ route('siswa.jadwal') }}"
|
||||
class="sb-link {{ request()->routeIs('siswa.jadwal') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-calendar-alt"></i></span>
|
||||
Jadwal
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('siswa.nilai') }}"
|
||||
class="sb-link {{ request()->routeIs('siswa.nilai') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-clipboard-list"></i></span>
|
||||
Nilai
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('siswa.tugas.index') }}"
|
||||
class="sb-link {{ request()->routeIs('siswa.tugas.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-tasks"></i></span>
|
||||
Tugas
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('siswa.ujian.index') }}"
|
||||
class="sb-link {{ request()->routeIs('siswa.ujian.*') ? 'active' : '' }}">
|
||||
<span class="sb-icon"><i class="fa-solid fa-file-alt"></i></span>
|
||||
Ujian
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('siswa/jadwal') ? 'active' : '' }}"
|
||||
href="{{ route('siswa.jadwal') }}">
|
||||
<i class="fas fa-calendar"></i> Jadwal
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('siswa/nilai') ? 'active' : '' }}"
|
||||
href="{{ route('siswa.nilai') }}">
|
||||
<i class="fas fa-clipboard-list"></i> Nilai
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- ✅ TUGAS (SUDAH BENAR) -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('siswa/tugas*') ? 'active' : '' }}"
|
||||
href="{{ route('siswa.tugas.index') }}">
|
||||
<i class="fas fa-tasks"></i> Tugas
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('siswa.ujian.index') }}" class="nav-link">
|
||||
<i class="fas fa-edit"></i> Ujian
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('siswa.absensi*') ? 'active' : '' }}"
|
||||
href="{{ route('siswa.absensi') }}">
|
||||
<i class="fas fa-user-check"></i> Absensi
|
||||
</a>
|
||||
</li> -->
|
||||
|
||||
</ul>
|
||||
<div class="sb-footer">
|
||||
<div class="sb-role-card">
|
||||
<div class="sb-role-avatar">
|
||||
{{ strtoupper(substr(auth()->user()->name, 0, 2)) }}
|
||||
</div>
|
||||
<div class="sb-role-info">
|
||||
<div class="sb-role-name">{{ auth()->user()->name }}</div>
|
||||
<div class="sb-role-label">Siswa</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{{-- supaya konten tidak ketutup navbar --}}
|
||||
<style>
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
</style>
|
||||
</aside>
|
||||
|
||||
@endif
|
||||
|
|
@ -1,206 +1,545 @@
|
|||
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
|
||||
{{-- ================================================================
|
||||
FILE: resources/views/layouts/topbar.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
<!-- Sidebar Toggle (Topbar) -->
|
||||
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<!-- Topbar Search -->
|
||||
<form
|
||||
class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-light border-0 small" placeholder="Search for..."
|
||||
aria-label="Search" aria-describedby="basic-addon2">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-info" type="button">
|
||||
<i class="fas fa-search fa-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
/* ── TOPBAR SHELL ──────────────────────────────────── */
|
||||
.custom-topbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 240px; /* sama dengan lebar sidebar */
|
||||
right: 0;
|
||||
height: 60px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.07);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 24px;
|
||||
z-index: 900;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
<!-- Topbar Navbar -->
|
||||
<ul class="navbar-nav ml-auto">
|
||||
/* ── LEFT: SEARCH ──────────────────────────────────── */
|
||||
.tb-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
background: #f4f6fa;
|
||||
border: 1.5px solid rgba(0,0,0,0.07);
|
||||
border-radius: 9px;
|
||||
padding: 7px 14px;
|
||||
width: 260px;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
|
||||
<!-- Nav Item - Search Dropdown (Visible Only XS) -->
|
||||
<li class="nav-item dropdown no-arrow d-sm-none">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-search fa-fw"></i>
|
||||
</a>
|
||||
<!-- Dropdown - Messages -->
|
||||
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in"
|
||||
aria-labelledby="searchDropdown">
|
||||
<form class="form-inline mr-auto w-100 navbar-search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-light border-0 small"
|
||||
placeholder="Search for..." aria-label="Search"
|
||||
aria-describedby="basic-addon2">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="button">
|
||||
<i class="fas fa-search fa-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
.tb-search:focus-within {
|
||||
border-color: rgba(26,86,219,0.4);
|
||||
box-shadow: 0 0 0 3px rgba(26,86,219,0.08);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
<!-- Nav Item - Alerts -->
|
||||
<li class="nav-item dropdown no-arrow mx-1">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-bell fa-fw"></i>
|
||||
<!-- Counter - Alerts -->
|
||||
<span class="badge badge-danger badge-counter">3+</span>
|
||||
</a>
|
||||
<!-- Dropdown - Alerts -->
|
||||
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="alertsDropdown">
|
||||
<h6 class="dropdown-header">
|
||||
Alerts Center
|
||||
</h6>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-primary">
|
||||
<i class="fas fa-file-alt text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 12, 2019</div>
|
||||
<span class="font-weight-bold">A new monthly report is ready to download!</span>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-success">
|
||||
<i class="fas fa-donate text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 7, 2019</div>
|
||||
$290.29 has been deposited into your account!
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="mr-3">
|
||||
<div class="icon-circle bg-warning">
|
||||
<i class="fas fa-exclamation-triangle text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="small text-gray-500">December 2, 2019</div>
|
||||
Spending Alert: We've noticed unusually high spending for your account.
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item text-center small text-gray-500" href="#">Show All Alerts</a>
|
||||
</div>
|
||||
</li>
|
||||
.tb-search i {
|
||||
font-size: 13px;
|
||||
color: #9ca3af;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
<!-- Nav Item - Messages -->
|
||||
<li class="nav-item dropdown no-arrow mx-1">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="messagesDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-envelope fa-fw"></i>
|
||||
<!-- Counter - Messages -->
|
||||
<span class="badge badge-danger badge-counter">7</span>
|
||||
</a>
|
||||
<!-- Dropdown - Messages -->
|
||||
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="messagesDropdown">
|
||||
<h6 class="dropdown-header">
|
||||
Message Center
|
||||
</h6>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="{{ asset('/') }}sbadmin2/img/undraw_profile_1.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-success"></div>
|
||||
</div>
|
||||
<div class="font-weight-bold">
|
||||
<div class="text-truncate">Hi there! I am wondering if you can help me with a
|
||||
problem I've been having.</div>
|
||||
<div class="small text-gray-500">Emily Fowler · 58m</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="{{ asset('/') }}sbadmin2/img/undraw_profile_2.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">I have the photos that you ordered last month, how
|
||||
would you like them sent to you?</div>
|
||||
<div class="small text-gray-500">Jae Chun · 1d</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="{{ asset('/') }}sbadmin2/img/undraw_profile_3.svg"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-warning"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">Last month's report looks great, I am very happy with
|
||||
the progress so far, keep up the good work!</div>
|
||||
<div class="small text-gray-500">Morgan Alvarez · 2d</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item d-flex align-items-center" href="#">
|
||||
<div class="dropdown-list-image mr-3">
|
||||
<img class="rounded-circle" src="{{ asset('/') }}sbadmin2/https://source.unsplash.com/Mv9hjnEUHR4/60x60"
|
||||
alt="...">
|
||||
<div class="status-indicator bg-success"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-truncate">Am I a good boy? The reason I ask is because someone
|
||||
told me that people say this to all dogs, even if they aren't good...</div>
|
||||
<div class="small text-gray-500">Chicken the Dog · 2w</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item text-center small text-gray-500" href="#">Read More Messages</a>
|
||||
</div>
|
||||
</li>
|
||||
.tb-search input {
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
font-size: 13px;
|
||||
color: #111827;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
<div class="topbar-divider d-none d-sm-block"></div>
|
||||
.tb-search input::placeholder { color: #9ca3af; }
|
||||
|
||||
<!-- Nav Item - User Information -->
|
||||
<li class="nav-item dropdown no-arrow">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
/* ── RIGHT: ACTIONS ────────────────────────────────── */
|
||||
.tb-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
<span class="mr-2 d-none d-lg-inline text-gray-600 small">
|
||||
{{ auth()->user()->name ?? 'Guest' }}
|
||||
</span>
|
||||
/* ── ICON BUTTON ───────────────────────────────────── */
|
||||
.tb-icon-btn {
|
||||
position: relative;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #6b7280;
|
||||
background: transparent;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
transition: background 0.13s, color 0.13s;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
@if(auth()->check() && auth()->user()->photo)
|
||||
<img class="img-profile rounded-circle"
|
||||
src="{{ asset('uploads/' . auth()->user()->photo) }}">
|
||||
@else
|
||||
<img class="img-profile rounded-circle"
|
||||
src="{{ asset('sbadmin2/img/undraw_profile.svg') }}">
|
||||
@endif
|
||||
.tb-icon-btn:hover {
|
||||
background: #f4f6fa;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
||||
aria-labelledby="userDropdown">
|
||||
.tb-badge {
|
||||
position: absolute;
|
||||
top: 5px; right: 5px;
|
||||
width: 16px; height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #dc2626;
|
||||
color: #fff;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
<a class="dropdown-item" href="{{ route('admin.profile') }}">
|
||||
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Profile
|
||||
</a>
|
||||
/* ── DIVIDER ───────────────────────────────────────── */
|
||||
.tb-sep {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: rgba(0,0,0,0.08);
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
<div class="dropdown-divider"></div>
|
||||
/* ── USER BUTTON ───────────────────────────────────── */
|
||||
.tb-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
padding: 5px 10px 5px 6px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
border: 1.5px solid transparent;
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
transition: background 0.13s, border-color 0.13s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
<a class="dropdown-item" href="#"
|
||||
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
|
||||
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
|
||||
Logout
|
||||
</a>
|
||||
.tb-user:hover {
|
||||
background: #f4f6fa;
|
||||
border-color: rgba(0,0,0,0.07);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tb-user-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid rgba(26,86,219,0.15);
|
||||
}
|
||||
|
||||
.tb-user-avatar-text {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #1a56db, #6366f1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid rgba(26,86,219,0.2);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.tb-user-info { line-height: 1.2; }
|
||||
|
||||
.tb-user-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
white-space: nowrap;
|
||||
max-width: 120px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tb-user-role {
|
||||
font-size: 11px;
|
||||
color: #9ca3af;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tb-user-caret {
|
||||
font-size: 10px;
|
||||
color: #9ca3af;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* ── DROPDOWN SHARED ───────────────────────────────── */
|
||||
.tb-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0,0,0,0.08);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.10), 0 2px 6px rgba(0,0,0,0.06);
|
||||
min-width: 280px;
|
||||
z-index: 1100;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transform: translateY(-6px);
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.tb-dropdown.open {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.tb-dd-header {
|
||||
padding: 12px 16px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #9ca3af;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
/* ── NOTIFICATION ITEMS ────────────────────────────── */
|
||||
.tb-notif-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 11px;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
transition: background 0.12s;
|
||||
}
|
||||
|
||||
.tb-notif-item:last-of-type { border-bottom: none; }
|
||||
|
||||
.tb-notif-item:hover { background: #f8fafc; text-decoration: none; }
|
||||
|
||||
.tb-notif-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
flex-shrink: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tb-notif-icon.blue { background: linear-gradient(135deg, #1a56db, #3b82f6); }
|
||||
.tb-notif-icon.green { background: linear-gradient(135deg, #0d9488, #10b981); }
|
||||
.tb-notif-icon.yellow { background: linear-gradient(135deg, #d97706, #f59e0b); }
|
||||
|
||||
.tb-notif-body { flex: 1; min-width: 0; }
|
||||
|
||||
.tb-notif-text {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #111827;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tb-notif-time {
|
||||
font-size: 11px;
|
||||
color: #9ca3af;
|
||||
margin-top: 3px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tb-dd-footer {
|
||||
padding: 10px 16px;
|
||||
text-align: center;
|
||||
border-top: 1px solid rgba(0,0,0,0.06);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1a56db;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
transition: background 0.12s;
|
||||
}
|
||||
|
||||
.tb-dd-footer:hover { background: #f4f6fa; text-decoration: none; }
|
||||
|
||||
/* ── USER DROPDOWN ─────────────────────────────────── */
|
||||
.tb-user-dd { min-width: 200px; }
|
||||
|
||||
.tb-user-dd-profile {
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
}
|
||||
|
||||
.tb-user-dd-avatar {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #1a56db, #6366f1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tb-user-dd-name { font-size: 13.5px; font-weight: 700; color: #111827; }
|
||||
.tb-user-dd-role { font-size: 11px; color: #9ca3af; font-weight: 500; }
|
||||
|
||||
.tb-user-dd-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.12s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
.tb-user-dd-item:hover { background: #f4f6fa; color: #111827; text-decoration: none; }
|
||||
.tb-user-dd-item.danger { color: #dc2626; }
|
||||
.tb-user-dd-item.danger:hover { background: #fff5f5; }
|
||||
.tb-user-dd-item i { font-size: 13px; width: 16px; text-align: center; color: #9ca3af; }
|
||||
.tb-user-dd-item.danger i { color: #dc2626; }
|
||||
|
||||
.tb-user-dd-divider { height: 1px; background: rgba(0,0,0,0.06); margin: 4px 0; }
|
||||
|
||||
/* ── WRAPPER for dropdown trigger ─────────────────── */
|
||||
.tb-dropdown-wrap { position: relative; }
|
||||
|
||||
/* ── MOBILE toggle ─────────────────────────────────── */
|
||||
.tb-mobile-toggle {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 9px;
|
||||
border: none;
|
||||
background: #f4f6fa;
|
||||
color: #374151;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.custom-topbar { left: 0; padding: 0 16px; }
|
||||
.tb-search { display: none; }
|
||||
.tb-mobile-toggle { display: flex; }
|
||||
.tb-user-name, .tb-user-role, .tb-user-caret { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<nav class="custom-topbar">
|
||||
|
||||
{{-- LEFT --}}
|
||||
<div style="display:flex; align-items:center; gap:12px;">
|
||||
{{-- Mobile sidebar toggle --}}
|
||||
<button class="tb-mobile-toggle" id="sidebarToggleTop">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
|
||||
{{-- Search --}}
|
||||
<div class="tb-search">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" placeholder="Cari sesuatu...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- RIGHT --}}
|
||||
<div class="tb-right">
|
||||
|
||||
{{-- NOTIFIKASI --}}
|
||||
<div class="tb-dropdown-wrap">
|
||||
<button class="tb-icon-btn" id="btnNotif" onclick="toggleDropdown('ddNotif', this)">
|
||||
<i class="fas fa-bell"></i>
|
||||
<span class="tb-badge">3</span>
|
||||
</button>
|
||||
|
||||
<div class="tb-dropdown" id="ddNotif">
|
||||
<div class="tb-dd-header">Notifikasi</div>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon blue"><i class="fas fa-file-alt"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Laporan bulanan baru siap diunduh</div>
|
||||
<div class="tb-notif-time">2 jam yang lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon green"><i class="fas fa-user-graduate"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Siswa baru telah terdaftar ke sistem</div>
|
||||
<div class="tb-notif-time">5 jam yang lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon yellow"><i class="fas fa-exclamation-triangle"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Jadwal minggu depan belum dikonfirmasi</div>
|
||||
<div class="tb-notif-time">1 hari yang lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-dd-footer">Lihat Semua Notifikasi →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- PESAN --}}
|
||||
<div class="tb-dropdown-wrap">
|
||||
<button class="tb-icon-btn" id="btnMsg" onclick="toggleDropdown('ddMsg', this)">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<span class="tb-badge">7</span>
|
||||
</button>
|
||||
|
||||
<div class="tb-dropdown" id="ddMsg">
|
||||
<div class="tb-dd-header">Pesan</div>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon blue"><i class="fas fa-user"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Ada pertanyaan mengenai jadwal pelajaran minggu ini</div>
|
||||
<div class="tb-notif-time">Emily Fowler · 58 menit lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon green"><i class="fas fa-user"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Foto tugas bulan lalu sudah siap, mau dikirim bagaimana?</div>
|
||||
<div class="tb-notif-time">Jae Chun · 1 hari lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-notif-item">
|
||||
<div class="tb-notif-icon yellow"><i class="fas fa-user"></i></div>
|
||||
<div class="tb-notif-body">
|
||||
<div class="tb-notif-text">Laporan bulan ini terlihat bagus, perkembangannya sangat baik!</div>
|
||||
<div class="tb-notif-time">Morgan Alvarez · 2 hari lalu</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" class="tb-dd-footer">Baca Semua Pesan →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tb-sep"></div>
|
||||
|
||||
{{-- USER MENU --}}
|
||||
<div class="tb-dropdown-wrap">
|
||||
<a href="#" class="tb-user" id="btnUser" onclick="toggleDropdown('ddUser', this); return false;">
|
||||
|
||||
@if(auth()->check() && auth()->user()->photo)
|
||||
<img class="tb-user-avatar"
|
||||
src="{{ asset('uploads/' . auth()->user()->photo) }}"
|
||||
alt="{{ auth()->user()->name }}">
|
||||
@else
|
||||
<div class="tb-user-avatar-text">
|
||||
{{ strtoupper(substr(auth()->user()->name ?? 'U', 0, 2)) }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="tb-user-info">
|
||||
<span class="tb-user-name">{{ auth()->user()->name ?? 'Guest' }}</span>
|
||||
<span class="tb-user-role">{{ ucfirst(auth()->user()->role ?? '') }}</span>
|
||||
</div>
|
||||
|
||||
<i class="fas fa-chevron-down tb-user-caret"></i>
|
||||
</a>
|
||||
|
||||
<div class="tb-dropdown tb-user-dd" id="ddUser">
|
||||
|
||||
{{-- Profile summary --}}
|
||||
<div class="tb-user-dd-profile">
|
||||
<div class="tb-user-dd-avatar">
|
||||
{{ strtoupper(substr(auth()->user()->name ?? 'U', 0, 2)) }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="tb-user-dd-name">{{ auth()->user()->name ?? 'Guest' }}</div>
|
||||
<div class="tb-user-dd-role">{{ ucfirst(auth()->user()->role ?? '') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Menu items --}}
|
||||
<a href="{{ route('admin.profile') }}" class="tb-user-dd-item">
|
||||
<i class="fas fa-user"></i> Profil Saya
|
||||
</a>
|
||||
|
||||
<div class="tb-user-dd-divider"></div>
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display:none;">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
<button class="tb-user-dd-item danger"
|
||||
onclick="document.getElementById('logout-form').submit()">
|
||||
<i class="fas fa-sign-out-alt"></i> Keluar
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</nav>
|
||||
<script>
|
||||
function toggleDropdown(id, trigger) {
|
||||
const dd = document.getElementById(id);
|
||||
const allDDs = document.querySelectorAll('.tb-dropdown');
|
||||
|
||||
allDDs.forEach(el => {
|
||||
if (el.id !== id) el.classList.remove('open');
|
||||
});
|
||||
|
||||
dd.classList.toggle('open');
|
||||
|
||||
// close on outside click
|
||||
const handler = (e) => {
|
||||
if (!trigger.contains(e.target) && !dd.contains(e.target)) {
|
||||
dd.classList.remove('open');
|
||||
document.removeEventListener('click', handler);
|
||||
}
|
||||
};
|
||||
|
||||
if (dd.classList.contains('open')) {
|
||||
setTimeout(() => document.addEventListener('click', handler), 0);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,163 +1,575 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container mt-4">
|
||||
|
||||
<h4 class="fw-bold mb-4">📊 Manajemen Absensi Guru</h4>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow p-4 mb-4">
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-light: #eff6ff;
|
||||
--primary-border: #bfdbfe;
|
||||
--primary-dark: #1d4ed8;
|
||||
--success: #16a34a;
|
||||
--success-light: #f0fdf4;
|
||||
--success-border: #bbf7d0;
|
||||
--warning: #d97706;
|
||||
--warning-light: #fffbeb;
|
||||
--warning-border: #fde68a;
|
||||
--danger: #dc2626;
|
||||
--danger-light: #fef2f2;
|
||||
--danger-border: #fecaca;
|
||||
--info: #0284c7;
|
||||
--info-light: #f0f9ff;
|
||||
--info-border: #bae6fd;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-secondary: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
|
||||
--shadow-md: 0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus: 0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px; --radius-xl: 18px;
|
||||
}
|
||||
|
||||
<form method="GET" action="{{ route('guru.absensi') }}">
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<div class="row">
|
||||
.page-wrapper { animation: fadeUp 0.4s ease both; }
|
||||
@keyframes fadeUp { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="fw-bold">Kelas</label>
|
||||
<select name="kelas_id" class="form-control" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
/* HEADER */
|
||||
.page-header { display:flex; align-items:center; gap:14px; margin-bottom:28px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--primary); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
|
||||
/* CARD */
|
||||
.section-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; margin-bottom:22px; }
|
||||
.card-head { padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); display:flex; align-items:center; gap:9px; }
|
||||
.card-head-dot { width:8px; height:8px; border-radius:50%; }
|
||||
.dot-blue { background:var(--primary); }
|
||||
.dot-green { background:var(--success); }
|
||||
.dot-orange { background:var(--warning); }
|
||||
.card-head-label { font-size:12.5px; font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.7px; }
|
||||
.card-head-label span { font-weight:600; color:var(--primary); text-transform:none; font-size:13px; }
|
||||
.card-body-p { padding:24px 22px; }
|
||||
|
||||
/* FORM FILTER */
|
||||
.filter-grid { display:grid; grid-template-columns:1fr 1fr 1fr; gap:16px; margin-bottom:20px; }
|
||||
.form-label { display:block; font-size:13px; font-weight:600; color:var(--text-primary); margin-bottom:6px; }
|
||||
.form-control, .form-select {
|
||||
width:100%; padding:10px 14px; font-size:13.5px;
|
||||
font-family:'Plus Jakarta Sans',sans-serif; color:var(--text-primary);
|
||||
background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none;
|
||||
transition:border-color 0.2s, box-shadow 0.2s; appearance:none;
|
||||
}
|
||||
.form-control:focus, .form-select:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.form-control[readonly] { background:var(--surface-secondary); color:var(--text-secondary); }
|
||||
.form-select {
|
||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat:no-repeat; background-position:right 12px center; background-size:16px; padding-right:38px; cursor:pointer;
|
||||
}
|
||||
|
||||
.btn-row { display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:8px; }
|
||||
|
||||
.btn-outline-primary {
|
||||
display:inline-flex; align-items:center; justify-content:center; gap:8px;
|
||||
padding:10px 20px; font-size:13.5px; font-weight:600;
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--surface); color:var(--primary);
|
||||
border:1.5px solid var(--primary); border-radius:var(--radius-md);
|
||||
cursor:pointer; transition:all 0.15s; text-decoration:none;
|
||||
}
|
||||
.btn-outline-primary:hover { background:var(--primary-light); }
|
||||
.btn-outline-primary svg { width:15px; height:15px; }
|
||||
|
||||
.btn-primary {
|
||||
display:inline-flex; align-items:center; justify-content:center; gap:8px;
|
||||
padding:10px 20px; font-size:13.5px; font-weight:600;
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--primary); color:#fff;
|
||||
border:none; border-radius:var(--radius-md);
|
||||
cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(37,99,235,0.25);
|
||||
}
|
||||
.btn-primary:hover { background:var(--primary-dark); }
|
||||
.btn-primary svg { width:15px; height:15px; }
|
||||
|
||||
/* TABLE */
|
||||
.table-responsive { overflow-x:auto; }
|
||||
table.styled-table { width:100%; border-collapse:collapse; min-width:500px; }
|
||||
.styled-table thead tr { background:var(--surface-secondary); border-bottom:1px solid var(--border); }
|
||||
.styled-table thead th { padding:12px 16px; font-size:11px; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.7px; text-align:left; white-space:nowrap; }
|
||||
.styled-table tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
||||
.styled-table tbody tr:last-child { border-bottom:none; }
|
||||
.styled-table tbody tr:hover { background:#f8faff; }
|
||||
.styled-table tbody td { padding:14px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
||||
|
||||
/* NAME CELL */
|
||||
.name-cell { display:flex; align-items:center; gap:10px; }
|
||||
.name-avatar { width:34px; height:34px; border-radius:50%; background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:var(--primary); flex-shrink:0; }
|
||||
|
||||
/* BADGES */
|
||||
.badge { display:inline-flex; align-items:center; gap:5px; border-radius:999px; font-size:12px; font-weight:600; padding:4px 12px; }
|
||||
.badge::before { content:''; width:6px; height:6px; border-radius:50%; flex-shrink:0; }
|
||||
.badge-hadir { background:var(--success-light); color:var(--success); border:1px solid var(--success-border); }
|
||||
.badge-hadir::before { background:var(--success); }
|
||||
.badge-izin { background:var(--warning-light); color:var(--warning); border:1px solid var(--warning-border); }
|
||||
.badge-izin::before { background:var(--warning); }
|
||||
.badge-sakit { background:var(--info-light); color:var(--info); border:1px solid var(--info-border); }
|
||||
.badge-sakit::before { background:var(--info); }
|
||||
.badge-alpha { background:var(--danger-light); color:var(--danger); border:1px solid var(--danger-border); }
|
||||
.badge-alpha::before { background:var(--danger); }
|
||||
|
||||
.ket-text { font-size:13px; color:var(--text-secondary); }
|
||||
|
||||
/* ACTION BUTTONS */
|
||||
.btn-edit-sm {
|
||||
display:inline-flex; align-items:center; gap:5px;
|
||||
padding:5px 12px; font-size:12.5px; font-weight:600;
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--warning-light); color:var(--warning);
|
||||
border:1px solid var(--warning-border); border-radius:var(--radius-sm);
|
||||
cursor:pointer; transition:all 0.15s;
|
||||
}
|
||||
.btn-edit-sm:hover { background:#fef3c7; }
|
||||
.btn-edit-sm svg { width:13px; height:13px; }
|
||||
|
||||
/* SEMUA HADIR BUTTON */
|
||||
.btn-all-hadir {
|
||||
display:inline-flex; align-items:center; gap:6px;
|
||||
padding:7px 15px; font-size:12.5px; font-weight:600;
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--success-light); color:var(--success);
|
||||
border:1px solid var(--success-border); border-radius:var(--radius-md);
|
||||
cursor:pointer; transition:all 0.15s;
|
||||
}
|
||||
.btn-all-hadir:hover { background:#dcfce7; }
|
||||
.btn-all-hadir svg { width:14px; height:14px; }
|
||||
|
||||
.btn-save-absen {
|
||||
display:flex; align-items:center; justify-content:center; gap:8px;
|
||||
width:100%; padding:12px; font-size:14px; font-weight:700;
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--success); color:#fff;
|
||||
border:none; border-radius:var(--radius-md);
|
||||
cursor:pointer; transition:all 0.15s;
|
||||
margin-top:20px; box-shadow:0 2px 8px rgba(22,163,74,0.3);
|
||||
}
|
||||
.btn-save-absen:hover { background:#15803d; }
|
||||
.btn-save-absen svg { width:16px; height:16px; }
|
||||
|
||||
/* INPUT TABLE */
|
||||
.styled-table select.status {
|
||||
padding:7px 10px; font-size:13px; border:1px solid var(--border);
|
||||
border-radius:var(--radius-sm); background:var(--surface);
|
||||
font-family:'Plus Jakarta Sans',sans-serif; outline:none;
|
||||
appearance:none;
|
||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat:no-repeat; background-position:right 8px center; background-size:14px;
|
||||
padding-right:30px; width:100%;
|
||||
}
|
||||
.styled-table input.form-control { padding:7px 10px; font-size:13px; }
|
||||
|
||||
/* TOOLBAR */
|
||||
.input-toolbar { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
|
||||
.input-toolbar-label { font-size:13px; color:var(--text-muted); }
|
||||
|
||||
/* ALERT */
|
||||
.alert-warn {
|
||||
background:var(--warning-light); border:1px solid var(--warning-border);
|
||||
border-radius:var(--radius-md); padding:14px 18px; text-align:center;
|
||||
font-size:13.5px; color:var(--warning); font-weight:500;
|
||||
}
|
||||
|
||||
/* EMPTY STATE */
|
||||
.empty-state { padding:60px 24px; text-align:center; }
|
||||
.empty-icon { width:60px; height:60px; background:var(--surface-secondary); border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; margin:0 auto 14px; }
|
||||
.empty-icon svg { width:26px; height:26px; color:var(--text-muted); }
|
||||
.empty-title { font-size:15px; font-weight:600; color:var(--text-primary); margin-bottom:4px; }
|
||||
.empty-desc { font-size:13px; color:var(--text-muted); }
|
||||
|
||||
/* ── CUSTOM MODAL (tanpa Bootstrap JS dependency) ── */
|
||||
.sw-modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
.sw-modal-overlay.active {
|
||||
display: flex;
|
||||
animation: overlayIn 0.2s ease;
|
||||
}
|
||||
@keyframes overlayIn { from { opacity:0; } to { opacity:1; } }
|
||||
|
||||
.sw-modal-box {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
overflow: hidden;
|
||||
animation: modalIn 0.25s ease;
|
||||
}
|
||||
@keyframes modalIn { from { opacity:0; transform:translateY(-16px) scale(0.97); } to { opacity:1; transform:translateY(0) scale(1); } }
|
||||
|
||||
.sw-modal-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 22px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
}
|
||||
.sw-modal-title {
|
||||
font-size: 15px; font-weight: 700; color: var(--text-primary); margin: 0;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.sw-modal-title svg { width:18px; height:18px; color:var(--warning); }
|
||||
.sw-modal-close {
|
||||
width: 30px; height: 30px; border-radius: 50%;
|
||||
background: var(--border); border: none; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--text-secondary); transition: all 0.15s;
|
||||
}
|
||||
.sw-modal-close:hover { background: var(--danger-light); color: var(--danger); }
|
||||
|
||||
.sw-modal-body { padding: 22px; }
|
||||
.sw-modal-field { margin-bottom: 16px; }
|
||||
.sw-modal-field:last-child { margin-bottom: 0; }
|
||||
.sw-modal-label {
|
||||
display: block; font-size: 13px; font-weight: 600;
|
||||
color: var(--text-primary); margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.sw-modal-foot {
|
||||
display: flex; align-items: center; justify-content: flex-end; gap: 10px;
|
||||
padding: 16px 22px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
}
|
||||
.btn-modal-cancel {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 9px 18px; font-size: 13.5px; font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--surface); color: var(--text-secondary);
|
||||
border: 1px solid var(--border); border-radius: var(--radius-md);
|
||||
cursor: pointer; transition: all 0.15s;
|
||||
}
|
||||
.btn-modal-cancel:hover { background: var(--surface-secondary); }
|
||||
.btn-modal-save {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 9px 22px; font-size: 13.5px; font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
background: var(--primary); color: #fff;
|
||||
border: none; border-radius: var(--radius-md);
|
||||
cursor: pointer; transition: all 0.15s;
|
||||
box-shadow: 0 2px 8px rgba(37,99,235,0.25);
|
||||
}
|
||||
.btn-modal-save:hover { background: var(--primary-dark); }
|
||||
|
||||
@media(max-width:768px) {
|
||||
.filter-grid { grid-template-columns:1fr; }
|
||||
.btn-row { grid-template-columns:1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container mt-4 page-wrapper">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="page-title">Manajemen Absensi</h4>
|
||||
<p class="page-subtitle">Catat dan pantau kehadiran siswa per kelas dan mata pelajaran</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="fw-bold">Mapel</label>
|
||||
{{-- FILTER CARD --}}
|
||||
<div class="section-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot dot-blue"></div>
|
||||
<span class="card-head-label">Filter Absensi</span>
|
||||
</div>
|
||||
<div class="card-body-p">
|
||||
<form method="GET" action="{{ route('guru.absensi') }}">
|
||||
<div class="filter-grid">
|
||||
<div>
|
||||
<label class="form-label">Kelas</label>
|
||||
<select name="kelas_id" class="form-select" required>
|
||||
<option value="">Pilih Kelas</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Mata Pelajaran</label>
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
<input type="text" class="form-control" value="{{ $mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Tanggal</label>
|
||||
<input type="date" name="tanggal" class="form-control"
|
||||
value="{{ request('tanggal') ?? date('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button type="submit" name="aksi" value="lihat" class="btn-outline-primary">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
Lihat Riwayat
|
||||
</button>
|
||||
<button type="submit" name="aksi" value="input" class="btn-primary">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
Input Absensi
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
{{-- ===== RIWAYAT ===== --}}
|
||||
@if(request('aksi') == 'lihat' && isset($riwayat))
|
||||
<div class="section-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot dot-orange"></div>
|
||||
<span class="card-head-label">Riwayat Absensi — <span>{{ request('tanggal') }}</span></span>
|
||||
</div>
|
||||
<div class="card-body-p" style="padding:0;">
|
||||
@if($riwayat && $riwayat->count())
|
||||
<div class="table-responsive">
|
||||
<table class="styled-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama Siswa</th>
|
||||
<th>Status</th>
|
||||
<th>Keterangan</th>
|
||||
<th style="text-align:center;">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($riwayat as $r)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="name-cell">
|
||||
<div class="name-avatar">{{ strtoupper(substr($r->siswa->nama,0,2)) }}</div>
|
||||
<span style="font-weight:600;">{{ $r->siswa->nama }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge
|
||||
@if($r->status=='hadir') badge-hadir
|
||||
@elseif($r->status=='izin') badge-izin
|
||||
@elseif($r->status=='sakit') badge-sakit
|
||||
@else badge-alpha
|
||||
@endif">
|
||||
{{ ucfirst($r->status) }}
|
||||
</span>
|
||||
</td>
|
||||
<td><span class="ket-text">{{ $r->keterangan ?? '—' }}</span></td>
|
||||
<td style="text-align:center;">
|
||||
{{-- Tombol edit → buka custom modal --}}
|
||||
<button class="btn-edit-sm"
|
||||
onclick="openEditModal(
|
||||
{{ $r->id }},
|
||||
'{{ addslashes($r->siswa->nama) }}',
|
||||
'{{ $r->status }}',
|
||||
'{{ addslashes($r->keterangan ?? '') }}'
|
||||
)">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/>
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="card-body-p">
|
||||
<div class="alert-warn">Tidak ada data absensi pada tanggal ini.</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
{{-- ===== FORM INPUT ===== --}}
|
||||
@if(request('aksi') == 'input' && isset($siswa))
|
||||
<div class="section-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot dot-green"></div>
|
||||
<span class="card-head-label">Input Absensi — <span>{{ request('tanggal') }}</span></span>
|
||||
</div>
|
||||
<div class="card-body-p">
|
||||
<form method="POST" action="{{ route('guru.absensi.simpan') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="kelas_id" value="{{ $kelasAktif->id }}">
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
<input type="hidden" name="tanggal" value="{{ request('tanggal') }}">
|
||||
|
||||
<div class="input-toolbar">
|
||||
<span class="input-toolbar-label">{{ $siswa->count() }} siswa ditemukan</span>
|
||||
<button type="button" class="btn-all-hadir" onclick="setAll('hadir')">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Semua Hadir
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="styled-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama Siswa</th>
|
||||
<th style="width:180px;">Status</th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($siswa as $s)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="name-cell">
|
||||
<div class="name-avatar">{{ strtoupper(substr($s->nama,0,2)) }}</div>
|
||||
<span style="font-weight:600;">{{ $s->nama }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<select name="absensi[{{ $s->id }}][status]" class="status">
|
||||
<option value="hadir">Hadir</option>
|
||||
<option value="izin">Izin</option>
|
||||
<option value="sakit">Sakit</option>
|
||||
<option value="alpha">Alpha</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="absensi[{{ $s->id }}][keterangan]" class="form-control" placeholder="Opsional...">
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-save-absen">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Simpan Absensi
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100">
|
||||
▶ Mulai Absensi
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- 🔥 TAMPIL FORM ABSENSI --}}
|
||||
@if(isset($siswa))
|
||||
|
||||
<div class="card shadow p-4 mb-4">
|
||||
|
||||
<h5 class="fw-bold mb-3">
|
||||
📋 Absensi Kelas {{ $kelasAktif->nama_kelas }}
|
||||
</h5>
|
||||
|
||||
<form method="POST" action="{{ route('guru.absensi.simpan') }}">
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="kelas_id" value="{{ $kelasAktif->id }}">
|
||||
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="fw-bold">Mapel</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="setAll('hadir')">
|
||||
✔ Semua Hadir
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<th>Status</th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($siswa as $s)
|
||||
<tr>
|
||||
<td>{{ $s->nama }}</td>
|
||||
|
||||
<td>
|
||||
<select name="absensi[{{ $s->id }}][status]" class="form-control status">
|
||||
<option value="hadir">Hadir</option>
|
||||
<option value="izin">Izin</option>
|
||||
<option value="sakit">Sakit</option>
|
||||
<option value="alpha">Alpha</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text"
|
||||
name="absensi[{{ $s->id }}][keterangan]"
|
||||
class="form-control"
|
||||
placeholder="Opsional...">
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<button class="btn btn-primary w-100 mt-3">
|
||||
💾 Simpan Absensi
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
{{-- 🔥 DATA ABSENSI --}}
|
||||
@if(isset($riwayat))
|
||||
|
||||
<div class="card shadow p-4">
|
||||
|
||||
<h5 class="fw-bold mb-3">📊 Riwayat Absensi</h5>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<th>Status</th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@forelse($riwayat as $r)
|
||||
<tr>
|
||||
<td>{{ $r->siswa->nama }}</td>
|
||||
<td>{{ ucfirst($r->status) }}</td>
|
||||
<td>{{ $r->keterangan }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="3" class="text-center text-muted">Belum ada data</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
{{-- ══ CUSTOM MODAL EDIT (satu modal, diisi via JS) ══ --}}
|
||||
<div class="sw-modal-overlay" id="editModalOverlay" onclick="closeEditModal(event)">
|
||||
<div class="sw-modal-box" onclick="event.stopPropagation()">
|
||||
<div class="sw-modal-head">
|
||||
<h5 class="sw-modal-title">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/>
|
||||
</svg>
|
||||
Edit Absensi Siswa
|
||||
</h5>
|
||||
<button class="sw-modal-close" onclick="closeEditModal()">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="editAbsensiForm" method="POST" action="">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="sw-modal-body">
|
||||
<div class="sw-modal-field">
|
||||
<label class="sw-modal-label">Nama Siswa</label>
|
||||
<input type="text" id="modalNama" class="form-control" readonly>
|
||||
</div>
|
||||
<div class="sw-modal-field">
|
||||
<label class="sw-modal-label">Status Kehadiran</label>
|
||||
<select id="modalStatus" name="status" class="form-select">
|
||||
<option value="hadir">Hadir</option>
|
||||
<option value="izin">Izin</option>
|
||||
<option value="sakit">Sakit</option>
|
||||
<option value="alpha">Alpha</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sw-modal-field">
|
||||
<label class="sw-modal-label">Keterangan <span style="font-weight:400;color:var(--text-muted);">(opsional)</span></label>
|
||||
<input type="text" id="modalKeterangan" name="keterangan" class="form-control" placeholder="Tulis keterangan...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-modal-foot">
|
||||
<button type="button" class="btn-modal-cancel" onclick="closeEditModal()">Batal</button>
|
||||
<button type="submit" class="btn-modal-save">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function setAll(status) {
|
||||
document.querySelectorAll('.status').forEach(el => {
|
||||
el.value = status;
|
||||
});
|
||||
document.querySelectorAll('.status').forEach(el => { el.value = status; });
|
||||
}
|
||||
|
||||
function openEditModal(id, nama, status, keterangan) {
|
||||
// Set action URL dinamis
|
||||
document.getElementById('editAbsensiForm').action = '/guru/absensi/update/' + id;
|
||||
|
||||
// Isi field
|
||||
document.getElementById('modalNama').value = nama;
|
||||
document.getElementById('modalStatus').value = status;
|
||||
document.getElementById('modalKeterangan').value = keterangan;
|
||||
|
||||
// Tampilkan overlay
|
||||
document.getElementById('editModalOverlay').classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeEditModal(event) {
|
||||
// Kalau klik overlay (bukan modal box), tutup
|
||||
if (event && event.target !== document.getElementById('editModalOverlay')) return;
|
||||
document.getElementById('editModalOverlay').classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Tutup dengan tombol ESC
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
document.getElementById('editModalOverlay').classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,25 +1,435 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Dashboard Admin</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body text-center">
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<h4>Selamat Datang 👑</h4>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
@auth
|
||||
{{ auth()->user()->name }}
|
||||
@endauth
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
<p class="text-muted">
|
||||
Anda login sebagai <b>Admin</b>
|
||||
</p>
|
||||
/* ── HERO BANNER ── */
|
||||
.sw-hero {
|
||||
background: linear-gradient(135deg, #1E3A6E 0%, #3730A3 50%, #4F46E5 100%);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 32px 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 28px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sw-hero::before {
|
||||
content: '';
|
||||
position: absolute; right: -60px; top: -60px;
|
||||
width: 280px; height: 280px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.08);
|
||||
}
|
||||
.sw-hero::after {
|
||||
content: '';
|
||||
position: absolute; right: 80px; bottom: -80px;
|
||||
width: 200px; height: 200px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.06);
|
||||
}
|
||||
|
||||
.sw-hero-left { position: relative; z-index: 1; }
|
||||
.sw-hero-greeting {
|
||||
font-size: 13px; font-weight: 600;
|
||||
color: rgba(255,255,255,.6);
|
||||
text-transform: uppercase; letter-spacing: 1px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sw-hero-name {
|
||||
font-size: 26px; font-weight: 800;
|
||||
color: #fff; line-height: 1.2;
|
||||
margin-bottom: 10px; letter-spacing: -.4px;
|
||||
}
|
||||
.sw-hero-meta {
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
.sw-hero-badge {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: rgba(255,255,255,.12);
|
||||
border: 1px solid rgba(255,255,255,.2);
|
||||
border-radius: 99px; padding: 5px 14px;
|
||||
font-size: 12px; font-weight: 600; color: rgba(255,255,255,.9);
|
||||
}
|
||||
.sw-hero-badge svg { opacity: .8; }
|
||||
|
||||
.sw-hero-right {
|
||||
position: relative; z-index: 1;
|
||||
text-align: right; flex-shrink: 0;
|
||||
}
|
||||
.sw-hero-date {
|
||||
font-size: 12px; color: rgba(255,255,255,.55);
|
||||
font-weight: 500; margin-bottom: 4px;
|
||||
}
|
||||
.sw-hero-time {
|
||||
font-size: 28px; font-weight: 800;
|
||||
color: #fff; letter-spacing: -1px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ── STATS GRID ── */
|
||||
.sw-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
|
||||
|
||||
.sw-stat-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.04);
|
||||
padding: 22px 20px;
|
||||
display: flex; align-items: center; gap: 16px;
|
||||
position: relative; overflow: hidden;
|
||||
transition: transform .2s, box-shadow .2s;
|
||||
}
|
||||
.sw-stat-card:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.07); }
|
||||
|
||||
.sw-stat-card::after {
|
||||
content: '';
|
||||
position: absolute; top: 0; left: 0; right: 0;
|
||||
height: 3px; border-radius: 99px 99px 0 0;
|
||||
}
|
||||
.sw-stat-card.primary::after { background: var(--primary); }
|
||||
.sw-stat-card.success::after { background: var(--success); }
|
||||
.sw-stat-card.warning::after { background: var(--warning); }
|
||||
.sw-stat-card.danger::after { background: var(--danger); }
|
||||
|
||||
.sw-stat-icon {
|
||||
width: 48px; height: 48px; border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sw-stat-icon.primary { background: var(--primary-light); color: var(--primary); }
|
||||
.sw-stat-icon.success { background: var(--success-light); color: var(--success); }
|
||||
.sw-stat-icon.warning { background: var(--warning-light); color: var(--warning); }
|
||||
.sw-stat-icon.danger { background: var(--danger-light); color: var(--danger); }
|
||||
|
||||
.sw-stat-val {
|
||||
font-size: 28px; font-weight: 800;
|
||||
color: var(--text-dark); line-height: 1;
|
||||
margin-bottom: 4px; font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.sw-stat-lbl { font-size: 12.5px; font-weight: 600; color: var(--text-soft); }
|
||||
|
||||
/* ── TWO-COL GRID ── */
|
||||
.sw-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }
|
||||
.sw-row-3 { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 20px; }
|
||||
|
||||
/* ── CARD BASE ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
.sw-card-hd {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.sw-card-hd-left { display: flex; align-items: center; gap: 10px; }
|
||||
.sw-card-hd-icon {
|
||||
width: 34px; height: 34px; border-radius: 9px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sw-card-hd-icon.primary { background: var(--primary-light); color: var(--primary); }
|
||||
.sw-card-hd-icon.success { background: var(--success-light); color: var(--success); }
|
||||
.sw-card-hd-icon.warning { background: var(--warning-light); color: var(--warning); }
|
||||
.sw-card-hd-icon.danger { background: var(--danger-light); color: var(--danger); }
|
||||
.sw-card-hd h6 { font-size: 14px; font-weight: 700; color: var(--text-dark); margin: 0; }
|
||||
.sw-card-hd p { font-size: 11.5px; color: var(--text-soft); margin: 2px 0 0; }
|
||||
.sw-card-bd { padding: 20px; }
|
||||
|
||||
/* ── QUICK ACTIONS ── */
|
||||
.sw-actions { display: flex; flex-direction: column; gap: 10px; padding: 20px; }
|
||||
|
||||
.sw-action-btn {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-radius: var(--radius-md);
|
||||
text-decoration: none;
|
||||
transition: all .18s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.sw-action-btn.success { background: var(--success-light); border-color: #A7F3D0; color: var(--success); }
|
||||
.sw-action-btn.success:hover { background: var(--success); color: #fff; border-color: var(--success); }
|
||||
.sw-action-btn.warning { background: var(--warning-light); border-color: #FDE68A; color: var(--warning); }
|
||||
.sw-action-btn.warning:hover { background: var(--warning); color: #fff; border-color: var(--warning); }
|
||||
.sw-action-btn.danger { background: var(--danger-light); border-color: #FECACA; color: var(--danger); }
|
||||
.sw-action-btn.danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); }
|
||||
|
||||
.sw-action-icon {
|
||||
width: 38px; height: 38px; border-radius: 9px;
|
||||
background: rgba(255,255,255,.6);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; transition: background .18s;
|
||||
}
|
||||
.sw-action-btn:hover .sw-action-icon { background: rgba(255,255,255,.2); }
|
||||
.sw-action-text strong { display: block; font-size: 13.5px; font-weight: 700; }
|
||||
.sw-action-text span { font-size: 11.5px; opacity: .75; }
|
||||
.sw-action-btn svg.arrow { margin-left: auto; opacity: .5; }
|
||||
|
||||
/* ── INFO LIST ── */
|
||||
.sw-info-list { display: flex; flex-direction: column; gap: 0; }
|
||||
.sw-info-row {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 13px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.sw-info-row:last-child { border-bottom: none; }
|
||||
.sw-info-row-label { color: var(--text-soft); font-weight: 500; display: flex; align-items: center; gap: 8px; }
|
||||
.sw-info-row-val { font-weight: 700; color: var(--text-dark); }
|
||||
|
||||
/* ── BADGE ── */
|
||||
.sw-badge {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 4px 10px; border-radius: 99px;
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
}
|
||||
.sw-badge-primary { background: var(--primary-light); color: var(--primary); }
|
||||
.sw-badge-success { background: var(--success-light); color: var(--success); }
|
||||
|
||||
/* ── MAPEL HIGHLIGHT ── */
|
||||
.sw-mapel-box {
|
||||
background: linear-gradient(135deg, var(--primary-light), #DDD6FE);
|
||||
border: 1px solid #C7D2FE;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 18px 20px;
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
}
|
||||
.sw-mapel-icon {
|
||||
width: 48px; height: 48px; border-radius: 12px;
|
||||
background: var(--primary); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; box-shadow: 0 4px 12px rgba(79,70,229,.3);
|
||||
}
|
||||
.sw-mapel-label { font-size: 11.5px; font-weight: 600; color: var(--primary); text-transform: uppercase; letter-spacing: .6px; }
|
||||
.sw-mapel-name { font-size: 17px; font-weight: 800; color: var(--primary-dark); margin-top: 3px; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sw-stats { grid-template-columns: repeat(2, 1fr); }
|
||||
.sw-row, .sw-row-3 { grid-template-columns: 1fr; }
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-hero { flex-direction: column; text-align: center; }
|
||||
.sw-hero-right { text-align: center; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- ── HERO BANNER ── --}}
|
||||
<div class="sw-hero">
|
||||
<div class="sw-hero-left">
|
||||
<div class="sw-hero-greeting">Selamat Datang</div>
|
||||
<div class="sw-hero-name">{{ auth()->user()->name }}</div>
|
||||
<div class="sw-hero-meta">
|
||||
<div class="sw-hero-badge">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
Guru
|
||||
</div>
|
||||
@if(auth()->user()->guru)
|
||||
<div class="sw-hero-badge">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
{{ auth()->user()->guru->mapel->nama_mapel ?? '-' }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-hero-right">
|
||||
<div class="sw-hero-date" id="sw-date"></div>
|
||||
<div class="sw-hero-time" id="sw-time"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── STATS ── --}}
|
||||
<div class="sw-stats">
|
||||
|
||||
<div class="sw-stat-card primary">
|
||||
<div class="sw-stat-icon primary">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $jumlahJadwal ?? 0 }}</div>
|
||||
<div class="sw-stat-lbl">Jadwal Mengajar</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-stat-card success">
|
||||
<div class="sw-stat-icon success">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><polyline points="23 11 17 17 14 14"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $jumlahAbsensi ?? 0 }}</div>
|
||||
<div class="sw-stat-lbl">Absensi Hari Ini</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-stat-card warning">
|
||||
<div class="sw-stat-icon warning">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $jumlahTugas ?? 0 }}</div>
|
||||
<div class="sw-stat-lbl">Tugas Dibuat</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-stat-card danger">
|
||||
<div class="sw-stat-icon danger">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $jumlahUjian ?? 0 }}</div>
|
||||
<div class="sw-stat-lbl">Ujian</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- ── ROW: QUICK ACTIONS + MAPEL INFO ── --}}
|
||||
<div class="sw-row-3">
|
||||
|
||||
{{-- QUICK ACTIONS --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-left">
|
||||
<div class="sw-card-hd-icon primary">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h6>Aksi Cepat</h6>
|
||||
<p>Kelola aktivitas dengan mudah</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-actions">
|
||||
<a href="{{ route('guru.absensi') }}" class="sw-action-btn success">
|
||||
<div class="sw-action-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><polyline points="23 11 17 17 14 14"/></svg>
|
||||
</div>
|
||||
<div class="sw-action-text">
|
||||
<strong>Input Absensi</strong>
|
||||
<span>Catat kehadiran siswa hari ini</span>
|
||||
</div>
|
||||
<svg class="arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
|
||||
</a>
|
||||
<a href="{{ route('guru.tugas.index') }}" class="sw-action-btn warning">
|
||||
<div class="sw-action-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
</div>
|
||||
<div class="sw-action-text">
|
||||
<strong>Kelola Tugas</strong>
|
||||
<span>Buat dan pantau tugas siswa</span>
|
||||
</div>
|
||||
<svg class="arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
|
||||
</a>
|
||||
<a href="{{ route('guru.ujian.index') }}" class="sw-action-btn danger">
|
||||
<div class="sw-action-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div class="sw-action-text">
|
||||
<strong>Kelola Ujian</strong>
|
||||
<span>Buat soal dan jadwal ujian</span>
|
||||
</div>
|
||||
<svg class="arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- INFO GURU --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-left">
|
||||
<div class="sw-card-hd-icon primary">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h6>Info Saya</h6>
|
||||
<p>Data profil guru</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-card-bd">
|
||||
|
||||
@if(auth()->user()->guru)
|
||||
<div class="sw-mapel-box" style="margin-bottom:16px;">
|
||||
<div class="sw-mapel-icon">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-mapel-label">Mata Pelajaran</div>
|
||||
<div class="sw-mapel-name">{{ auth()->user()->guru->mapel->nama_mapel ?? '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="sw-info-list">
|
||||
<div class="sw-info-row">
|
||||
<div class="sw-info-row-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
Role
|
||||
</div>
|
||||
<span class="sw-badge sw-badge-primary">Guru</span>
|
||||
</div>
|
||||
<div class="sw-info-row">
|
||||
<div class="sw-info-row-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
Status
|
||||
</div>
|
||||
<span class="sw-badge sw-badge-success">Aktif</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updateClock() {
|
||||
const now = new Date();
|
||||
const days = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'];
|
||||
const months = ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'];
|
||||
const dateEl = document.getElementById('sw-date');
|
||||
const timeEl = document.getElementById('sw-time');
|
||||
if (dateEl) dateEl.textContent = days[now.getDay()] + ', ' + now.getDate() + ' ' + months[now.getMonth()] + ' ' + now.getFullYear();
|
||||
if (timeEl) timeEl.textContent = String(now.getHours()).padStart(2,'0') + ':' + String(now.getMinutes()).padStart(2,'0');
|
||||
}
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,38 +1,512 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Jadwal Mengajar</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary: #2563eb;
|
||||
--primary-light: #eff6ff;
|
||||
--primary-border: #bfdbfe;
|
||||
--success: #16a34a;
|
||||
--success-light: #f0fdf4;
|
||||
--success-border: #bbf7d0;
|
||||
--info: #0284c7;
|
||||
--info-light: #f0f9ff;
|
||||
--info-border: #bae6fd;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-secondary: #f8fafc;
|
||||
--border: #e2e8f0;
|
||||
--shadow-md: 0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-xl: 18px;
|
||||
}
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kelas</th>
|
||||
<th>Mapel</th>
|
||||
<th>Hari</th>
|
||||
<th>Jam</th>
|
||||
</tr>
|
||||
</thead>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
<tbody>
|
||||
@foreach($jadwal as $j)
|
||||
<tr>
|
||||
<td>{{ $j->kelas->nama_kelas }}</td>
|
||||
<td>{{ $j->mapel->nama_mapel }}</td>
|
||||
<td>{{ $j->hari }}</td>
|
||||
<td>{{ $j->jam_mulai }} - {{ $j->jam_selesai }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
</table>
|
||||
.jadwal-wrapper {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
animation: fadeInUp 0.4s ease both;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ---- HEADER ---- */
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.page-icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--primary-light);
|
||||
border: 1px solid var(--primary-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.page-icon svg { width: 24px; height: 24px; color: var(--primary); }
|
||||
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 3px 0;
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 13.5px;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ---- STATS ROW ---- */
|
||||
.stats-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 18px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 16px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stat-icon.blue { background: var(--primary-light); }
|
||||
.stat-icon.green { background: var(--success-light); }
|
||||
.stat-icon.teal { background: var(--info-light); }
|
||||
.stat-icon svg { width: 18px; height: 18px; }
|
||||
.stat-icon.blue svg { color: var(--primary); }
|
||||
.stat-icon.green svg { color: var(--success); }
|
||||
.stat-icon.teal svg { color: var(--info); }
|
||||
|
||||
.stat-label { font-size: 12px; color: var(--text-muted); font-weight: 500; }
|
||||
.stat-value { font-size: 20px; font-weight: 700; color: var(--text-primary); line-height: 1.1; margin-top: 1px; }
|
||||
|
||||
/* ---- TABLE CARD ---- */
|
||||
.table-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 22px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-badge {
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
border: 1px solid var(--primary-border);
|
||||
border-radius: 999px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-wrap svg {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--text-muted);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 7px 12px 7px 32px;
|
||||
font-size: 13px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
color: var(--text-primary);
|
||||
width: 200px;
|
||||
outline: none;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(37,99,235,0.1);
|
||||
}
|
||||
|
||||
.search-input::placeholder { color: var(--text-muted); }
|
||||
|
||||
/* ---- TABLE ---- */
|
||||
.table-responsive { overflow-x: auto; }
|
||||
|
||||
table.jadwal-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 540px;
|
||||
}
|
||||
|
||||
.jadwal-table thead tr {
|
||||
background: var(--surface-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.jadwal-table thead th {
|
||||
padding: 12px 18px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.7px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jadwal-table thead th.center { text-align: center; }
|
||||
|
||||
.jadwal-table tbody tr {
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.jadwal-table tbody tr:last-child { border-bottom: none; }
|
||||
.jadwal-table tbody tr:hover { background: #f8faff; }
|
||||
|
||||
.jadwal-table tbody td {
|
||||
padding: 15px 18px;
|
||||
font-size: 13.5px;
|
||||
color: var(--text-primary);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jadwal-table tbody td.center { text-align: center; }
|
||||
|
||||
/* KELAS CELL */
|
||||
.kelas-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.kelas-dot {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
background: var(--primary-light);
|
||||
border: 1px solid var(--primary-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* MAPEL CELL */
|
||||
.mapel-text {
|
||||
font-size: 13.5px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* HARI BADGE */
|
||||
.badge-hari {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: var(--info-light);
|
||||
color: var(--info);
|
||||
border: 1px solid var(--info-border);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 5px 14px;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
/* JAM BADGE */
|
||||
.badge-jam {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: var(--success-light);
|
||||
color: var(--success);
|
||||
border: 1px solid var(--success-border);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 5px 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badge-jam svg { width: 13px; height: 13px; }
|
||||
|
||||
/* EMPTY STATE */
|
||||
.empty-state {
|
||||
padding: 72px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: var(--surface-secondary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.empty-icon svg { width: 28px; height: 28px; color: var(--text-muted); }
|
||||
.empty-title { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 6px; }
|
||||
.empty-desc { font-size: 13.5px; color: var(--text-muted); }
|
||||
|
||||
/* FOOTER */
|
||||
.table-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 13px 22px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary);
|
||||
font-size: 12.5px;
|
||||
color: var(--text-muted);
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.stats-row { grid-template-columns: 1fr 1fr; }
|
||||
.page-title { font-size: 18px; }
|
||||
.search-input { width: 160px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4 py-4">
|
||||
<div class="jadwal-wrapper">
|
||||
|
||||
{{-- PAGE HEADER --}}
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="page-title">Jadwal Mengajar</h1>
|
||||
<p class="page-subtitle">Daftar jadwal kelas dan mata pelajaran yang diampu</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATS --}}
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon blue">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="stat-label">Total Jadwal</div>
|
||||
<div class="stat-value">{{ $jadwal->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon green">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="stat-label">Kelas Diampu</div>
|
||||
<div class="stat-value">{{ $jadwal->unique(fn($j) => $j->kelas->nama_kelas)->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon teal">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="stat-label">Hari Aktif</div>
|
||||
<div class="stat-value">{{ $jadwal->unique('hari')->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- TABLE CARD --}}
|
||||
<div class="table-card">
|
||||
|
||||
<div class="table-toolbar">
|
||||
<div class="toolbar-title">
|
||||
Daftar Jadwal
|
||||
<span class="toolbar-badge">{{ $jadwal->count() }} sesi</span>
|
||||
</div>
|
||||
<div class="search-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35M17 11A6 6 0 115 11a6 6 0 0112 0z"/>
|
||||
</svg>
|
||||
<input type="text" class="search-input" placeholder="Cari kelas, hari..." id="searchInput" oninput="filterJadwal()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="jadwal-table" id="jadwalTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th class="center">Hari</th>
|
||||
<th class="center">Jam Mengajar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="jadwalBody">
|
||||
@forelse($jadwal as $j)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="kelas-pill">
|
||||
<div class="kelas-dot">
|
||||
{{ strtoupper(substr($j->kelas->nama_kelas, 0, 2)) }}
|
||||
</div>
|
||||
{{ $j->kelas->nama_kelas }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="mapel-text">{{ $j->mapel->nama_mapel }}</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
<span class="badge-hari">{{ $j->hari }}</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
<span class="badge-jam">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
{{ $j->jam_mulai }} – {{ $j->jam_selesai }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum ada jadwal tersedia</div>
|
||||
<div class="empty-desc">Jadwal mengajar akan muncul di sini setelah ditambahkan oleh admin.</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-footer">
|
||||
<span id="footerCount">Menampilkan {{ $jadwal->count() }} jadwal</span>
|
||||
<span>{{ now()->format('l, d F Y') }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function filterJadwal() {
|
||||
const q = document.getElementById('searchInput').value.toLowerCase();
|
||||
const rows = document.querySelectorAll('#jadwalBody tr');
|
||||
let count = 0;
|
||||
rows.forEach(row => {
|
||||
const show = row.textContent.toLowerCase().includes(q);
|
||||
row.style.display = show ? '' : 'none';
|
||||
if (show) count++;
|
||||
});
|
||||
document.getElementById('footerCount').textContent = 'Menampilkan ' + count + ' jadwal';
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,45 +1,173 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Edit Nilai</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
||||
--danger:#dc2626;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
*{
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
<form action="{{ route('guru.nilai.update', $nilai->id) }}" method="POST">
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea{
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
}
|
||||
.form-wrapper{
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
animation:fadeUp .4s ease both;
|
||||
}
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:translateY(0)} }
|
||||
|
||||
.back-nav { display:inline-flex; align-items:center; gap:6px; color:var(--text-muted); font-size:13.5px; font-weight:500; text-decoration:none; margin-bottom:22px; transition:color 0.15s; }
|
||||
.back-nav:hover { color:var(--primary); text-decoration:none; }
|
||||
.back-nav svg { width:16px; height:16px; }
|
||||
|
||||
.page-header { display:flex; align-items:center; gap:14px; margin-bottom:24px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--success-light); border:1px solid var(--success-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--success); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
|
||||
/* INFO BANNER */
|
||||
.siswa-banner { display:flex; align-items:center; gap:14px; background:var(--surface-secondary); border:1px solid var(--border); border-radius:var(--radius-lg); padding:16px 20px; margin-bottom:22px; }
|
||||
.siswa-avatar { width:44px; height:44px; border-radius:var(--radius-md); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; font-size:15px; font-weight:700; color:var(--primary); flex-shrink:0; }
|
||||
.siswa-name { font-size:14px; font-weight:600; color:var(--text-primary); }
|
||||
.siswa-meta { font-size:12.5px; color:var(--text-secondary); margin-top:2px; }
|
||||
|
||||
/* CARD */
|
||||
.form-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; }
|
||||
.card-head { padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); display:flex; align-items:center; gap:9px; }
|
||||
.card-head-dot { width:8px; height:8px; border-radius:50%; background:var(--success); }
|
||||
.card-head-label { font-size:12.5px; font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.7px; }
|
||||
.card-body-p { padding:26px 24px; }
|
||||
|
||||
.form-label { display:block; font-size:13px; font-weight:600; color:var(--text-primary); margin-bottom:7px; }
|
||||
.form-control {
|
||||
width:100%; padding:10px 14px; font-size:14px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
color:var(--text-primary); background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none; transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.form-control:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.form-control[readonly] { background:var(--surface-secondary); color:var(--text-secondary); }
|
||||
.mb-3 { margin-bottom:18px; }
|
||||
|
||||
/* NILAI INPUT SPECIAL */
|
||||
.nilai-input-wrap { position:relative; }
|
||||
.nilai-input-wrap input { font-size:18px; font-weight:700; padding:12px 50px 12px 16px; text-align:center; }
|
||||
.nilai-unit { position:absolute; right:14px; top:50%; transform:translateY(-50%); font-size:13px; color:var(--text-muted); font-weight:600; pointer-events:none; }
|
||||
|
||||
/* CARD FOOTER */
|
||||
.card-footer-p { display:flex; align-items:center; justify-content:space-between; padding:18px 24px; border-top:1px solid var(--border); background:var(--surface-secondary); flex-wrap:wrap; gap:10px; }
|
||||
|
||||
.btn-cancel { display:inline-flex; align-items:center; gap:7px; padding:10px 20px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--surface); color:var(--text-secondary); border:1px solid var(--border); border-radius:var(--radius-md); text-decoration:none; cursor:pointer; transition:all 0.15s; }
|
||||
.btn-cancel:hover { background:var(--surface-secondary); color:var(--text-primary); text-decoration:none; }
|
||||
.btn-cancel svg { width:15px; height:15px; }
|
||||
|
||||
.btn-save { display:inline-flex; align-items:center; gap:8px; padding:10px 26px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--success); color:#fff; border:none; border-radius:var(--radius-md); cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(22,163,74,0.3); }
|
||||
.btn-save:hover { background:#15803d; transform:translateY(-1px); }
|
||||
.btn-save svg { width:15px; height:15px; }
|
||||
|
||||
|
||||
.edit-page{
|
||||
width:100%;
|
||||
padding:24px 28px 40px;
|
||||
}
|
||||
|
||||
.form-grid{
|
||||
display:grid;
|
||||
grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
|
||||
gap:20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="edit-page">
|
||||
<div class="form-wrapper">
|
||||
|
||||
<a href="{{ route('guru.nilai.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||
Kembali ke Daftar Nilai
|
||||
</a>
|
||||
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="page-title">Edit Nilai</h2>
|
||||
<p class="page-subtitle">Perbarui nilai akademik siswa</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- SISWA BANNER --}}
|
||||
<div class="siswa-banner">
|
||||
<div class="siswa-avatar">{{ strtoupper(substr($nilai->siswa->nama, 0, 2)) }}</div>
|
||||
<div>
|
||||
<div class="siswa-name">{{ $nilai->siswa->nama }}</div>
|
||||
<div class="siswa-meta">{{ $nilai->mapel->nama_mapel }}</div>
|
||||
</div>
|
||||
<div style="margin-left:auto; text-align:right;">
|
||||
<div style="font-size:11px;color:var(--text-muted);font-weight:600;text-transform:uppercase;letter-spacing:0.5px;">Nilai Saat Ini</div>
|
||||
<div style="font-size:24px;font-weight:700;color:var(--primary);">{{ $nilai->nilai }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot"></div>
|
||||
<span class="card-head-label">Perbarui Nilai</span>
|
||||
</div>
|
||||
<div class="card-body-p">
|
||||
<form action="{{ route('guru.nilai.update', $nilai->id) }}" method="POST" id="formEditNilai">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Nama Siswa</label>
|
||||
<label class="form-label">Nama Siswa</label>
|
||||
<input type="text" class="form-control" value="{{ $nilai->siswa->nama }}" readonly>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Mata Pelajaran</label>
|
||||
<label class="form-label">Mata Pelajaran</label>
|
||||
<input type="text" class="form-control" value="{{ $nilai->mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Nilai</label>
|
||||
<input type="number" name="nilai" class="form-control"
|
||||
value="{{ $nilai->nilai }}" min="0" max="100" required>
|
||||
<label class="form-label">Nilai Baru <span style="color:var(--danger);">*</span></label>
|
||||
<div class="nilai-input-wrap">
|
||||
<input type="number" name="nilai" class="form-control" value="{{ $nilai->nilai }}" min="0" max="100" required>
|
||||
<span class="nilai-unit">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">
|
||||
Update Nilai
|
||||
</button>
|
||||
|
||||
<a href="{{ route('guru.nilai.index') }}" class="btn btn-secondary">
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="card-footer-p">
|
||||
<a href="{{ route('guru.nilai.index') }}" class="btn-cancel">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" form="formEditNilai" class="btn-save">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
Simpan Nilai
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,70 +1,215 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Header + Button -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 text-gray-800">Data Nilai Siswa</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<a href="{{ route('guru.nilai.input') }}" class="btn btn-primary">
|
||||
+ Input Nilai
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
||||
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
* { font-family:'Plus Jakarta Sans',sans-serif; box-sizing:border-box; }
|
||||
.page-wrapper { animation:fadeUp 0.4s ease both; }
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(12px)} to{opacity:1;transform:translateY(0)} }
|
||||
|
||||
/* HEADER */
|
||||
.page-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:28px; gap:16px; flex-wrap:wrap; }
|
||||
.page-header-left { display:flex; align-items:center; gap:14px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--primary); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
|
||||
/* BUTTON ADD */
|
||||
.btn-add {
|
||||
display:inline-flex; align-items:center; gap:7px; padding:10px 20px;
|
||||
font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--primary); color:#fff; border:none; border-radius:var(--radius-md);
|
||||
text-decoration:none; cursor:pointer; transition:all 0.15s;
|
||||
box-shadow:0 2px 8px rgba(37,99,235,0.25);
|
||||
}
|
||||
.btn-add:hover { background:var(--primary-dark); color:#fff; text-decoration:none; transform:translateY(-1px); }
|
||||
.btn-add svg { width:15px; height:15px; }
|
||||
|
||||
/* FILTER BAR */
|
||||
.filter-bar { display:flex; align-items:center; gap:12px; margin-bottom:0; }
|
||||
.filter-select {
|
||||
padding:8px 34px 8px 12px; font-size:13px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
border:1px solid var(--border); border-radius:var(--radius-md);
|
||||
background:var(--surface); color:var(--text-primary); outline:none;
|
||||
appearance:none; cursor:pointer; width:200px;
|
||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat:no-repeat; background-position:right 10px center; background-size:14px;
|
||||
transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.filter-select:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.filter-label { font-size:13px; font-weight:500; color:var(--text-secondary); }
|
||||
|
||||
/* CARD */
|
||||
.section-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; }
|
||||
.card-toolbar { display:flex; align-items:center; justify-content:space-between; padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); flex-wrap:wrap; gap:12px; }
|
||||
.toolbar-title { font-size:13.5px; font-weight:600; color:var(--text-primary); }
|
||||
.toolbar-badge { background:var(--primary-light); color:var(--primary); border:1px solid var(--primary-border); border-radius:999px; font-size:11.5px; font-weight:600; padding:2px 10px; margin-left:8px; }
|
||||
|
||||
/* TABLE */
|
||||
.table-responsive { overflow-x:auto; }
|
||||
table.nilai-table { width:100%; border-collapse:collapse; min-width:600px; }
|
||||
.nilai-table thead tr { background:var(--surface-secondary); border-bottom:1px solid var(--border); }
|
||||
.nilai-table thead th { padding:12px 16px; font-size:11px; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.7px; text-align:left; white-space:nowrap; }
|
||||
.nilai-table tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
||||
.nilai-table tbody tr:last-child { border-bottom:none; }
|
||||
.nilai-table tbody tr:hover { background:#f8faff; }
|
||||
.nilai-table tbody td { padding:14px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
||||
.cell-no { font-size:13px; color:var(--text-muted); font-weight:500; }
|
||||
|
||||
/* NAME CELL */
|
||||
.name-cell { display:flex; align-items:center; gap:10px; }
|
||||
.name-avatar { width:34px; height:34px; border-radius:50%; background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:var(--primary); flex-shrink:0; }
|
||||
|
||||
/* NILAI BADGE */
|
||||
.nilai-chip {
|
||||
display:inline-flex; align-items:center; justify-content:center;
|
||||
width:46px; height:28px; border-radius:var(--radius-sm);
|
||||
font-size:13px; font-weight:700;
|
||||
}
|
||||
.nilai-chip.tinggi { background:var(--success-light); color:var(--success); border:1px solid var(--success-border); }
|
||||
.nilai-chip.sedang { background:var(--warning-light); color:var(--warning); border:1px solid var(--warning-border); }
|
||||
.nilai-chip.rendah { background:var(--danger-light); color:var(--danger); border:1px solid var(--danger-border); }
|
||||
|
||||
.badge-kelas { background:var(--primary-light); color:var(--primary); border:1px solid var(--primary-border); border-radius:999px; font-size:11.5px; font-weight:600; padding:3px 11px; display:inline-flex; }
|
||||
.badge-mapel { background:var(--surface-secondary); color:var(--text-secondary); border:1px solid var(--border); border-radius:999px; font-size:11.5px; font-weight:600; padding:3px 11px; display:inline-flex; }
|
||||
|
||||
.btn-edit-sm {
|
||||
display:inline-flex; align-items:center; gap:5px; padding:5px 12px;
|
||||
font-size:12.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
background:var(--warning-light); color:var(--warning);
|
||||
border:1px solid var(--warning-border); border-radius:var(--radius-sm);
|
||||
text-decoration:none; transition:all 0.15s;
|
||||
}
|
||||
.btn-edit-sm:hover { background:#fef3c7; color:var(--warning); text-decoration:none; }
|
||||
.btn-edit-sm svg { width:13px; height:13px; }
|
||||
|
||||
/* EMPTY */
|
||||
.empty-state { padding:64px 24px; text-align:center; }
|
||||
.empty-icon { width:60px; height:60px; background:var(--surface-secondary); border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; margin:0 auto 14px; }
|
||||
.empty-icon svg { width:26px; height:26px; color:var(--text-muted); }
|
||||
.empty-title { font-size:15px; font-weight:600; color:var(--text-primary); margin-bottom:4px; }
|
||||
.empty-desc { font-size:13px; color:var(--text-muted); }
|
||||
|
||||
/* TABLE FOOTER */
|
||||
.table-footer { display:flex; align-items:center; justify-content:space-between; padding:13px 22px; border-top:1px solid var(--border); background:var(--surface-secondary); font-size:12.5px; color:var(--text-muted); flex-wrap:wrap; gap:6px; }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4 py-2 page-wrapper">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="page-header">
|
||||
<div class="page-header-left">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="page-title">Data Nilai Siswa</h4>
|
||||
<p class="page-subtitle">Kelola dan pantau nilai akademik siswa</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ route('guru.nilai.input') }}" class="btn-add">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
Input Nilai
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Card -->
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
{{-- TABLE CARD --}}
|
||||
<div class="section-card">
|
||||
<div class="card-toolbar">
|
||||
<span class="toolbar-title">Daftar Nilai<span class="toolbar-badge">{{ $nilai->count() }} data</span></span>
|
||||
<form method="GET" action="{{ route('guru.nilai.index') }}" style="margin:0;">
|
||||
<div class="filter-bar">
|
||||
<span class="filter-label">Filter:</span>
|
||||
<select name="kelas_id" class="filter-select" onchange="this.form.submit()">
|
||||
<option value="">Semua Kelas</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Nilai</th>
|
||||
<th>Aksi</th> <!-- 🔥 TAMBAH -->
|
||||
</tr>
|
||||
</thead>
|
||||
<div class="table-responsive">
|
||||
<table class="nilai-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Nilai</th>
|
||||
<th style="text-align:center;">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($nilai as $n)
|
||||
<tr>
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
<td>{{ $n->siswa->nama ?? '-' }}</td>
|
||||
<td>{{ $n->siswa->kelas->nama_kelas ?? '-' }}</td>
|
||||
<td>{{ $n->mapel->nama_mapel ?? '-' }}</td>
|
||||
<td>{{ $n->nilai }}</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ route('guru.nilai.edit', $n->id) }}" class="btn btn-warning btn-sm">
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
||||
<td>
|
||||
<div class="name-cell">
|
||||
<div class="name-avatar">{{ strtoupper(substr($n->siswa->nama ?? 'S', 0, 2)) }}</div>
|
||||
<span style="font-weight:600;">{{ $n->siswa->nama ?? '—' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge-kelas">{{ $n->siswa->kelas->nama_kelas ?? '—' }}</span></td>
|
||||
<td><span class="badge-mapel">{{ $n->mapel->nama_mapel ?? '—' }}</span></td>
|
||||
<td>
|
||||
<span class="nilai-chip {{ $n->nilai >= 80 ? 'tinggi' : ($n->nilai >= 60 ? 'sedang' : 'rendah') }}">
|
||||
{{ $n->nilai }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<a href="{{ route('guru.nilai.edit', $n->id) }}" class="btn-edit-sm">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/>
|
||||
</svg>
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">Belum ada data</td>
|
||||
<td colspan="6">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum ada data nilai</div>
|
||||
<div class="empty-desc">Mulai input nilai dengan klik tombol Input Nilai di atas.</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
<form method="GET" action="{{ route('guru.nilai.index') }}" class="mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<select name="kelas_id" class="form-control" onchange="this.form.submit()">
|
||||
<option value="">-- Semua Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-footer">
|
||||
<span>Menampilkan {{ $nilai->count() }} data nilai</span>
|
||||
<span>{{ now()->format('d M Y') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,104 +1,309 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Input Nilai Siswa</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--danger:#dc2626;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
* {
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
<!-- PILIH KELAS -->
|
||||
<div class="mb-3">
|
||||
<label>Kelas</label>
|
||||
<select id="kelas" class="form-control">
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
}
|
||||
.form-wrapper {
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
animation:fadeUp 0.4s ease both;
|
||||
}
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:translateY(0)} }
|
||||
|
||||
.back-nav { display:inline-flex; align-items:center; gap:6px; color:var(--text-muted); font-size:13.5px; font-weight:500; text-decoration:none; margin-bottom:22px; transition:color 0.15s; }
|
||||
.back-nav:hover { color:var(--primary); text-decoration:none; }
|
||||
.back-nav svg { width:16px; height:16px; }
|
||||
|
||||
.page-header { display:flex; align-items:center; gap:14px; margin-bottom:24px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--primary); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
|
||||
.form-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; margin-bottom:16px; }
|
||||
.card-head { padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); display:flex; align-items:center; gap:9px; }
|
||||
.card-head-dot { width:8px; height:8px; border-radius:50%; }
|
||||
.dot-blue { background:var(--primary); }
|
||||
.dot-green { background:var(--success); }
|
||||
.card-head-label { font-size:12.5px; font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.7px; }
|
||||
.card-body-p { padding:22px 24px; }
|
||||
|
||||
.form-label { display:block; font-size:13px; font-weight:600; color:var(--text-primary); margin-bottom:7px; }
|
||||
.form-control {
|
||||
width:100%; padding:10px 14px; font-size:13.5px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
color:var(--text-primary); background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none; transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.form-control:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.form-control[readonly] { background:var(--surface-secondary); color:var(--text-secondary); }
|
||||
.form-select {
|
||||
width:100%; padding:10px 38px 10px 14px; font-size:13.5px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
color:var(--text-primary); background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none; appearance:none; cursor:pointer;
|
||||
transition:border-color 0.2s, box-shadow 0.2s;
|
||||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat:no-repeat; background-position:right 12px center; background-size:16px;
|
||||
}
|
||||
.form-select:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.mb-3 { margin-bottom:16px; }
|
||||
|
||||
.filter-row {
|
||||
display:grid;
|
||||
grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
|
||||
gap:18px;
|
||||
}
|
||||
|
||||
/* TABLE */
|
||||
.table-responsive { overflow-x:auto; }
|
||||
table.nilai-input-table { width:100%; border-collapse:collapse; min-width:400px; }
|
||||
.nilai-input-table thead tr { background:var(--surface-secondary); border-bottom:1px solid var(--border); }
|
||||
.nilai-input-table thead th { padding:12px 16px; font-size:11px; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.7px; text-align:left; white-space:nowrap; }
|
||||
.nilai-input-table tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
||||
.nilai-input-table tbody tr:last-child { border-bottom:none; }
|
||||
.nilai-input-table tbody tr:hover { background:#f8faff; }
|
||||
.nilai-input-table tbody td { padding:12px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
||||
.nilai-page{
|
||||
width:100%;
|
||||
padding:24px 28px 40px;
|
||||
}
|
||||
.cell-no { font-size:12px; color:var(--text-muted); width:42px; }
|
||||
|
||||
.name-cell { display:flex; align-items:center; gap:10px; }
|
||||
.name-avatar { width:32px; height:32px; border-radius:50%; background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; font-size:10px; font-weight:700; color:var(--primary); flex-shrink:0; }
|
||||
|
||||
.nilai-input {
|
||||
width:90px; padding:7px 10px; font-size:14px; font-weight:700; text-align:center;
|
||||
font-family:'Plus Jakarta Sans',sans-serif; border:1px solid var(--border);
|
||||
border-radius:var(--radius-sm); outline:none; transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.nilai-input:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
|
||||
/* PLACEHOLDER STATE */
|
||||
.table-placeholder { padding:48px 24px; text-align:center; }
|
||||
.placeholder-icon { width:52px; height:52px; background:var(--surface-secondary); border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; margin:0 auto 12px; }
|
||||
.placeholder-icon svg { width:24px; height:24px; color:var(--text-muted); }
|
||||
.placeholder-text { font-size:13.5px; color:var(--text-muted); }
|
||||
|
||||
/* FOOTER */
|
||||
.card-footer-p { display:flex; align-items:center; justify-content:space-between; padding:18px 24px; border-top:1px solid var(--border); background:var(--surface-secondary); flex-wrap:wrap; gap:10px; }
|
||||
.btn-save { display:inline-flex; align-items:center; gap:8px; padding:10px 26px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--success); color:#fff; border:none; border-radius:var(--radius-md); cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(22,163,74,0.3); }
|
||||
.btn-save:hover { background:#15803d; transform:translateY(-1px); }
|
||||
.btn-save svg { width:15px; height:15px; }
|
||||
.footer-info { font-size:12.5px; color:var(--text-muted); }
|
||||
|
||||
.form-card{
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.table-responsive{
|
||||
width:100%;
|
||||
overflow-x:auto;
|
||||
}
|
||||
|
||||
.card-body-p{
|
||||
padding:28px;
|
||||
}
|
||||
|
||||
.card-footer-p{
|
||||
padding:20px 28px;
|
||||
}
|
||||
|
||||
@media (max-width:768px){
|
||||
|
||||
.nilai-page{
|
||||
padding:16px;
|
||||
}
|
||||
|
||||
.page-header{
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.page-title{
|
||||
font-size:20px;
|
||||
}
|
||||
|
||||
.filter-row{
|
||||
grid-template-columns:1fr;
|
||||
}
|
||||
|
||||
.card-body-p{
|
||||
padding:18px;
|
||||
}
|
||||
|
||||
.card-footer-p{
|
||||
padding:16px 18px;
|
||||
flex-direction:column;
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.btn-save{
|
||||
width:100%;
|
||||
justify-content:center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="nilai-page">
|
||||
<div class="form-wrapper">
|
||||
|
||||
<a href="{{ route('guru.nilai.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||
Kembali ke Daftar Nilai
|
||||
</a>
|
||||
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="page-title">Input Nilai Siswa</h2>
|
||||
<p class="page-subtitle">Masukkan nilai akademik siswa per kelas</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FILTER CARD --}}
|
||||
<div class="form-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot dot-blue"></div>
|
||||
<span class="card-head-label">Pilih Kelas</span>
|
||||
</div>
|
||||
<div class="card-body-p">
|
||||
<div class="filter-row">
|
||||
<div>
|
||||
<label class="form-label">Kelas</label>
|
||||
<select id="kelas" class="form-select">
|
||||
<option value="">— Pilih Kelas —</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Mata Pelajaran</label>
|
||||
<input type="text" class="form-control" value="{{ $mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 🔥 MAPEL OTOMATIS -->
|
||||
<div class="mb-3">
|
||||
<label>Mata Pelajaran</label>
|
||||
<input type="text" class="form-control" value="{{ $mapel->nama_mapel }}" readonly>
|
||||
</div>
|
||||
{{-- INPUT NILAI CARD --}}
|
||||
<div class="form-card">
|
||||
<div class="card-head">
|
||||
<div class="card-head-dot dot-green"></div>
|
||||
<span class="card-head-label">Input Nilai</span>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('guru.nilai.storeBatch') }}" method="POST">
|
||||
@csrf
|
||||
<form action="{{ route('guru.nilai.storeBatch') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
|
||||
<!-- 🔥 kirim mapel otomatis -->
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
|
||||
<table class="table table-bordered">
|
||||
<div class="table-responsive">
|
||||
<table class="nilai-input-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50">No</th>
|
||||
<th class="cell-no">No</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th width="200">Nilai</th>
|
||||
<th style="text-align:center;">Nilai</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="data-siswa">
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">Pilih kelas terlebih dahulu</td>
|
||||
<td colspan="3">
|
||||
<div class="table-placeholder">
|
||||
<div class="placeholder-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a4 4 0 00-5-3.87M9 20H4v-2a4 4 0 015-3.87m4 5.87a4 4 0 110-8 4 4 0 010 8zm6-12a4 4 0 10-8 0 4 4 0 008 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="placeholder-text">Pilih kelas terlebih dahulu untuk memuat daftar siswa</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mt-2">
|
||||
<div class="card-footer-p">
|
||||
<span class="footer-info" id="siswaCount">Pilih kelas untuk mulai</span>
|
||||
<button type="submit" class="btn-save" id="btnSave" style="display:none;">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
Simpan Nilai
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('kelas').addEventListener('change', function() {
|
||||
|
||||
let kelas_id = this.value;
|
||||
|
||||
if (!kelas_id) {
|
||||
document.getElementById('data-siswa').innerHTML =
|
||||
`<tr><td colspan="3" class="text-center">Pilih kelas terlebih dahulu</td></tr>`;
|
||||
document.getElementById('data-siswa').innerHTML = `
|
||||
<tr><td colspan="3"><div class="table-placeholder">
|
||||
<div class="placeholder-icon"><svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" style="width:24px;height:24px;color:var(--text-muted)"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a4 4 0 00-5-3.87M9 20H4v-2a4 4 0 015-3.87m4 5.87a4 4 0 110-8 4 4 0 010 8zm6-12a4 4 0 10-8 0 4 4 0 008 0z"/></svg></div>
|
||||
<p class="placeholder-text">Pilih kelas terlebih dahulu untuk memuat daftar siswa</p>
|
||||
</div></td></tr>`;
|
||||
document.getElementById('btnSave').style.display = 'none';
|
||||
document.getElementById('siswaCount').textContent = 'Pilih kelas untuk mulai';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/guru/get-siswa/' + kelas_id)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
let html = '';
|
||||
let no = 1;
|
||||
|
||||
if (data.length === 0) {
|
||||
html = `<tr><td colspan="3" class="text-center">Tidak ada siswa</td></tr>`;
|
||||
html = `<tr><td colspan="3"><div class="table-placeholder"><p class="placeholder-text">Tidak ada siswa di kelas ini</p></div></td></tr>`;
|
||||
document.getElementById('btnSave').style.display = 'none';
|
||||
document.getElementById('siswaCount').textContent = '0 siswa';
|
||||
} else {
|
||||
let no = 1;
|
||||
data.forEach(siswa => {
|
||||
const initials = siswa.nama.substring(0, 2).toUpperCase();
|
||||
html += `
|
||||
<tr>
|
||||
<td>${no++}</td>
|
||||
<td>${siswa.nama}</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
name="nilai[${siswa.id}]"
|
||||
class="form-control"
|
||||
min="0"
|
||||
max="100"
|
||||
required
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
<tr>
|
||||
<td class="cell-no">${no++}</td>
|
||||
<td>
|
||||
<div class="name-cell">
|
||||
<div class="name-avatar">${initials}</div>
|
||||
<span style="font-weight:600;">${siswa.nama}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<input type="number" name="nilai[${siswa.id}]" class="nilai-input" min="0" max="100" required placeholder="0">
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
document.getElementById('btnSave').style.display = 'inline-flex';
|
||||
document.getElementById('siswaCount').textContent = data.length + ' siswa siap diinput';
|
||||
}
|
||||
|
||||
document.getElementById('data-siswa').innerHTML = html;
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,83 +1,211 @@
|
|||
{{-- ============================================================
|
||||
FILE: tugas/create.blade.php
|
||||
============================================================ --}}
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
||||
--info:#0284c7; --info-light:#f0f9ff; --info-border:#bae6fd;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08); --shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
*{
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
<div class="mb-4">
|
||||
<h4 class="fw-bold mb-1">Tambah Tugas</h4>
|
||||
<small class="text-muted">Isi data tugas yang akan diberikan kepada siswa</small>
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea{
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
}
|
||||
.form-wrapper{
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
animation:fadeUp .4s ease both;
|
||||
}
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:translateY(0)} }
|
||||
.back-nav { display:inline-flex; align-items:center; gap:6px; color:var(--text-muted); font-size:13.5px; font-weight:500; text-decoration:none; margin-bottom:22px; transition:color 0.15s; }
|
||||
.back-nav:hover { color:var(--primary); text-decoration:none; }
|
||||
.back-nav svg { width:16px; height:16px; }
|
||||
.page-header { display:flex; align-items:center; gap:14px; margin-bottom:24px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--primary); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
.form-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; margin-bottom:16px; }
|
||||
.card-head { padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); display:flex; align-items:center; gap:9px; }
|
||||
.card-head-dot { width:8px; height:8px; border-radius:50%; }
|
||||
.dot-blue { background:var(--primary); } .dot-green { background:var(--success); }
|
||||
.card-head-label { font-size:12.5px; font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.7px; }
|
||||
.card-body-p { padding:24px; }
|
||||
.form-row { display:grid; gap:16px; margin-bottom:16px; }
|
||||
.form-row.cols-2{
|
||||
grid-template-columns:
|
||||
repeat(auto-fit,minmax(280px,1fr));
|
||||
}
|
||||
.form-row.cols-3{
|
||||
grid-template-columns:
|
||||
repeat(auto-fit,minmax(260px,1fr));
|
||||
}
|
||||
.form-row.cols-1 { grid-template-columns:1fr; }
|
||||
.form-label { display:block; font-size:13px; font-weight:600; color:var(--text-primary); margin-bottom:7px; }
|
||||
.form-control, .form-select, textarea.form-control {
|
||||
width:100%; padding:10px 14px; font-size:13.5px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
color:var(--text-primary); background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none; transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.form-control:focus, .form-select:focus, textarea.form-control:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.form-control[readonly] { background:var(--surface-secondary); color:var(--text-secondary); }
|
||||
.form-select { appearance:none; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 12px center; background-size:16px; padding-right:38px; cursor:pointer; }
|
||||
textarea.form-control { resize:vertical; min-height:100px; }
|
||||
.file-hint { font-size:12px; color:var(--text-muted); margin-top:5px; }
|
||||
.card-footer-p { display:flex; align-items:center; justify-content:space-between; padding:18px 24px; border-top:1px solid var(--border); background:var(--surface-secondary); flex-wrap:wrap; gap:10px; }
|
||||
.btn-cancel { display:inline-flex; align-items:center; gap:7px; padding:10px 20px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--surface); color:var(--text-secondary); border:1px solid var(--border); border-radius:var(--radius-md); text-decoration:none; cursor:pointer; transition:all 0.15s; }
|
||||
.btn-cancel:hover { background:var(--surface-secondary); color:var(--text-primary); text-decoration:none; }
|
||||
.btn-cancel svg { width:15px; height:15px; }
|
||||
.btn-save { display:inline-flex; align-items:center; gap:8px; padding:10px 26px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--primary); color:#fff; border:none; border-radius:var(--radius-md); cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(37,99,235,0.25); }
|
||||
.btn-save:hover { background:var(--primary-dark); transform:translateY(-1px); }
|
||||
.btn-save svg { width:15px; height:15px; }
|
||||
@media(max-width:768px){
|
||||
|
||||
.tugas-page{
|
||||
padding:16px;
|
||||
}
|
||||
|
||||
.page-header{
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.page-title{
|
||||
font-size:20px;
|
||||
}
|
||||
|
||||
.form-row.cols-2,
|
||||
.form-row.cols-3{
|
||||
grid-template-columns:1fr;
|
||||
}
|
||||
|
||||
.card-body-p{
|
||||
padding:18px;
|
||||
}
|
||||
|
||||
.card-footer-p{
|
||||
flex-direction:column;
|
||||
align-items:stretch;
|
||||
}
|
||||
|
||||
.btn-save,
|
||||
.btn-cancel{
|
||||
width:100%;
|
||||
justify-content:center;
|
||||
}
|
||||
}
|
||||
|
||||
.tugas-page{
|
||||
width:100%;
|
||||
padding:24px 28px 40px;
|
||||
}
|
||||
|
||||
.form-card{
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.card-body-p{
|
||||
padding:28px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="tugas-page">
|
||||
<div class="form-wrapper">
|
||||
|
||||
<a href="{{ route('guru.tugas.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||
Kembali ke Daftar Tugas
|
||||
</a>
|
||||
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="page-title">Tambah Tugas Baru</h2>
|
||||
<p class="page-subtitle">Buat tugas untuk siswa lengkap dengan file dan deadline</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
|
||||
<form action="{{ route('guru.tugas.store') }}" method="POST" enctype="multipart/form-data">
|
||||
<div class="form-card">
|
||||
<div class="card-head"><div class="card-head-dot dot-blue"></div><span class="card-head-label">Informasi Tugas</span></div>
|
||||
<div class="card-body-p">
|
||||
<form action="{{ route('guru.tugas.store') }}" method="POST" enctype="multipart/form-data" id="formCreate">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Judul Tugas</label>
|
||||
<input type="text" name="judul" class="form-control" required>
|
||||
<div class="form-row cols-3">
|
||||
<div>
|
||||
<label class="form-label">Judul Tugas <span style="color:var(--danger)">*</span></label>
|
||||
<input type="text" name="judul" class="form-control" placeholder="Masukkan judul tugas" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Kelas</label>
|
||||
<div>
|
||||
<label class="form-label">Kelas <span style="color:var(--danger)">*</span></label>
|
||||
<select name="kelas_id" class="form-select" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
<option value="">— Pilih Kelas —</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div>
|
||||
<label class="form-label">Mata Pelajaran</label>
|
||||
|
||||
@if($mapel)
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $mapel->nama_mapel }}"
|
||||
readonly>
|
||||
|
||||
<input type="text" class="form-control" value="{{ $mapel->nama_mapel }}" readonly>
|
||||
<input type="hidden" name="mapel_id" value="{{ $mapel->id }}">
|
||||
@else
|
||||
<input type="text" class="form-control"
|
||||
value="Mapel belum diset di akun guru" readonly>
|
||||
<input type="text" class="form-control" value="Mapel belum diset" readonly>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<div class="form-row cols-1">
|
||||
<div>
|
||||
<label class="form-label">Deskripsi</label>
|
||||
<textarea name="deskripsi" rows="4" class="form-control"></textarea>
|
||||
<textarea name="deskripsi" class="form-control" placeholder="Tuliskan deskripsi dan instruksi tugas..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 mb-3">
|
||||
<div class="form-row cols-2">
|
||||
<div>
|
||||
<label class="form-label">Upload File Tugas</label>
|
||||
<input type="file" name="file" class="form-control"
|
||||
accept=".pdf,.doc,.docx,.ppt,.pptx">
|
||||
<input type="file" name="file" class="form-control" accept=".pdf,.doc,.docx,.ppt,.pptx">
|
||||
<p class="file-hint">Format: PDF, DOC, DOCX, PPT, PPTX</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Deadline</label>
|
||||
<div>
|
||||
<label class="form-label">Deadline <span style="color:var(--danger)">*</span></label>
|
||||
<input type="datetime-local" name="deadline" class="form-control" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex justify-content-end">
|
||||
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary me-2">
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Simpan Tugas
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="card-footer-p">
|
||||
<a href="{{ route('guru.tugas.index') }}" class="btn-cancel">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" form="formCreate" class="btn-save">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
Simpan Tugas
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,80 +1,176 @@
|
|||
{{-- ============================================================
|
||||
FILE: tugas/edit.blade.php
|
||||
============================================================ --}}
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
||||
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08); --shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
*{
|
||||
box-sizing:border-box;
|
||||
}
|
||||
|
||||
<h4>Edit Tugas</h4>
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea{
|
||||
font-family:'Plus Jakarta Sans',sans-serif;
|
||||
}
|
||||
.form-wrapper{
|
||||
width:100%;
|
||||
max-width:100%;
|
||||
animation:fadeUp .4s ease both;
|
||||
}
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(14px)} to{opacity:1;transform:translateY(0)} }
|
||||
.back-nav { display:inline-flex; align-items:center; gap:6px; color:var(--text-muted); font-size:13.5px; font-weight:500; text-decoration:none; margin-bottom:22px; transition:color 0.15s; }
|
||||
.back-nav:hover { color:var(--primary); text-decoration:none; }
|
||||
.back-nav svg { width:16px; height:16px; }
|
||||
.page-header { display:flex; align-items:center; gap:14px; margin-bottom:24px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--warning-light); border:1px solid var(--warning-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--warning); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
.form-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; }
|
||||
.card-head { padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); display:flex; align-items:center; gap:9px; }
|
||||
.card-head-dot { width:8px; height:8px; border-radius:50%; background:var(--warning); }
|
||||
.card-head-label { font-size:12.5px; font-weight:700; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.7px; }
|
||||
.card-body-p { padding:24px; }
|
||||
.form-label { display:block; font-size:13px; font-weight:600; color:var(--text-primary); margin-bottom:7px; }
|
||||
.form-control, .form-select, textarea.form-control {
|
||||
width:100%; padding:10px 14px; font-size:13.5px; font-family:'Plus Jakarta Sans',sans-serif;
|
||||
color:var(--text-primary); background:var(--surface); border:1px solid var(--border);
|
||||
border-radius:var(--radius-md); outline:none; transition:border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.form-control:focus, .form-select:focus, textarea.form-control:focus { border-color:var(--primary); box-shadow:var(--shadow-focus); }
|
||||
.form-select { appearance:none; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 12px center; background-size:16px; padding-right:38px; cursor:pointer; }
|
||||
textarea.form-control { resize:vertical; min-height:90px; }
|
||||
.mb-3 { margin-bottom:16px; }
|
||||
.file-link { display:inline-flex; align-items:center; gap:7px; color:var(--primary); font-size:13px; font-weight:600; text-decoration:none; padding:7px 14px; background:var(--primary-light); border:1px solid var(--primary-border); border-radius:var(--radius-sm); margin-top:4px; }
|
||||
.file-link:hover { background:#dbeafe; text-decoration:none; color:var(--primary); }
|
||||
.file-link svg { width:14px; height:14px; }
|
||||
.file-hint { font-size:12px; color:var(--text-muted); margin-top:5px; }
|
||||
.alert-danger-c { background:var(--danger-light); border:1px solid var(--danger-border); border-radius:var(--radius-md); padding:14px 18px; margin-bottom:20px; font-size:13px; color:var(--danger); }
|
||||
.card-footer-p { display:flex; align-items:center; justify-content:space-between; padding:18px 24px; border-top:1px solid var(--border); background:var(--surface-secondary); flex-wrap:wrap; gap:10px; }
|
||||
.btn-cancel { display:inline-flex; align-items:center; gap:7px; padding:10px 20px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--surface); color:var(--text-secondary); border:1px solid var(--border); border-radius:var(--radius-md); text-decoration:none; cursor:pointer; transition:all 0.15s; }
|
||||
.btn-cancel:hover { background:var(--surface-secondary); color:var(--text-primary); text-decoration:none; }
|
||||
.btn-cancel svg { width:15px; height:15px; }
|
||||
.btn-update { display:inline-flex; align-items:center; gap:8px; padding:10px 26px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--warning); color:#fff; border:none; border-radius:var(--radius-md); cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(217,119,6,0.3); }
|
||||
.btn-update:hover { background:#b45309; transform:translateY(-1px); }
|
||||
.btn-update svg { width:15px; height:15px; }
|
||||
|
||||
<form action="{{ route('guru.tugas.update', $tugas->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
{{-- ERROR --}}
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
@foreach ($errors->all() as $error)
|
||||
<div>{{ $error }}</div>
|
||||
@endforeach
|
||||
.edit-tugas-page{
|
||||
width:100%;
|
||||
padding:24px 28px 40px;
|
||||
}
|
||||
|
||||
.form-grid{
|
||||
display:grid;
|
||||
grid-template-columns:
|
||||
repeat(auto-fit,minmax(320px,1fr));
|
||||
gap:20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="edit-tugas-page">
|
||||
<div class="form-wrapper">
|
||||
|
||||
<a href="{{ route('guru.tugas.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||
Kembali ke Daftar Tugas
|
||||
</a>
|
||||
|
||||
<div class="page-header">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="page-title">Edit Tugas</h2>
|
||||
<p class="page-subtitle">Perbarui informasi tugas yang sudah dibuat</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-card">
|
||||
<div class="card-head"><div class="card-head-dot"></div><span class="card-head-label">Perbarui Data Tugas</span></div>
|
||||
<div class="card-body-p">
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert-danger-c">
|
||||
@foreach ($errors->all() as $error)<div>{{ $error }}</div>@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- JUDUL --}}
|
||||
<div class="mb-3">
|
||||
<label>Judul</label>
|
||||
<input type="text" name="judul" class="form-control" value="{{ $tugas->judul }}" required>
|
||||
</div>
|
||||
|
||||
{{-- KELAS --}}
|
||||
<div class="mb-3">
|
||||
<label>Kelas</label>
|
||||
<select name="kelas_id" class="form-control" required>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}"
|
||||
{{ $tugas->kelas_id == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{{-- DESKRIPSI --}}
|
||||
<div class="mb-3">
|
||||
<label>Deskripsi</label>
|
||||
<textarea name="deskripsi" class="form-control">{{ $tugas->deskripsi }}</textarea>
|
||||
</div>
|
||||
|
||||
{{-- FILE LAMA --}}
|
||||
<div class="mb-3">
|
||||
<label>File Lama</label><br>
|
||||
|
||||
@if($tugas->file)
|
||||
<a href="{{ asset('storage/' . $tugas->file) }}" target="_blank">
|
||||
Lihat / Download File
|
||||
</a>
|
||||
@else
|
||||
<span class="text-muted">Tidak ada file</span>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('guru.tugas.update', $tugas->id) }}" method="POST" enctype="multipart/form-data" id="formEdit">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Judul Tugas <span style="color:var(--danger)">*</span></label>
|
||||
<input type="text" name="judul" class="form-control" value="{{ $tugas->judul }}" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kelas <span style="color:var(--danger)">*</span></label>
|
||||
<select name="kelas_id" class="form-select" required>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}" {{ $tugas->kelas_id == $k->id ? 'selected' : '' }}>{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Deskripsi</label>
|
||||
<textarea name="deskripsi" class="form-control">{{ $tugas->deskripsi }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">File Saat Ini</label><br>
|
||||
@if($tugas->file)
|
||||
<a href="{{ asset('storage/' . $tugas->file) }}" target="_blank" class="file-link">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
|
||||
Lihat / Download File
|
||||
</a>
|
||||
@else
|
||||
<span style="font-size:13px;color:var(--text-muted);">Tidak ada file terlampir</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Ganti File <span style="font-weight:400;color:var(--text-muted);">(opsional)</span></label>
|
||||
<input type="file" name="file" class="form-control" accept=".pdf,.doc,.docx,.ppt,.pptx">
|
||||
<p class="file-hint">Biarkan kosong jika tidak ingin mengganti file</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Deadline <span style="color:var(--danger)">*</span></label>
|
||||
<input type="datetime-local" name="deadline" class="form-control"
|
||||
value="{{ \Carbon\Carbon::parse($tugas->deadline)->format('Y-m-d\TH:i') }}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- GANTI FILE --}}
|
||||
<div class="mb-3">
|
||||
<label>Ganti File (opsional)</label>
|
||||
<input type="file" name="file" class="form-control"
|
||||
accept=".pdf,.doc,.docx,.ppt,.pptx">
|
||||
<div class="card-footer-p">
|
||||
<a href="{{ route('guru.tugas.index') }}" class="btn-cancel">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" form="formEdit" class="btn-update">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- DEADLINE (DATETIME) --}}
|
||||
<div class="mb-3">
|
||||
<label>Deadline</label>
|
||||
<input type="datetime-local" name="deadline"
|
||||
value="{{ \Carbon\Carbon::parse($tugas->deadline)->format('Y-m-d\TH:i') }}">
|
||||
</div>
|
||||
|
||||
{{-- BUTTON --}}
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary">Kembali</a>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,117 +1,381 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h4 class="mb-1 fw-bold">Manajemen Tugas</h4>
|
||||
<small class="text-muted">Kelola dan pantau tugas yang diberikan kepada siswa</small>
|
||||
</div>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
||||
|
||||
<a href="{{ route('guru.tugas.create') }}" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> Tambah Tugas
|
||||
</a>
|
||||
</div>
|
||||
:root {
|
||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
||||
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
||||
--info:#0284c7; --info-light:#f0f9ff; --info-border:#bae6fd;
|
||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||
}
|
||||
* { font-family:'Plus Jakarta Sans',sans-serif; box-sizing:border-box; }
|
||||
.page-wrapper { animation:fadeUp 0.4s ease both; }
|
||||
@keyframes fadeUp { from{opacity:0;transform:translateY(12px)} to{opacity:1;transform:translateY(0)} }
|
||||
|
||||
<!-- CARD -->
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
/* HEADER */
|
||||
.page-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:28px; gap:16px; flex-wrap:wrap; }
|
||||
.page-header-left { display:flex; align-items:center; gap:14px; }
|
||||
.page-icon { width:50px; height:50px; border-radius:var(--radius-lg); background:var(--primary-light); border:1px solid var(--primary-border); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
||||
.page-icon svg { width:24px; height:24px; color:var(--primary); }
|
||||
.page-title { font-size:22px; font-weight:700; color:var(--text-primary); margin:0 0 2px; letter-spacing:-0.3px; }
|
||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
.btn-add { display:inline-flex; align-items:center; gap:7px; padding:10px 20px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--primary); color:#fff; border:none; border-radius:var(--radius-md); text-decoration:none; cursor:pointer; transition:all 0.15s; box-shadow:0 2px 8px rgba(37,99,235,0.25); }
|
||||
.btn-add:hover { background:var(--primary-dark); color:#fff; text-decoration:none; transform:translateY(-1px); }
|
||||
.btn-add svg { width:15px; height:15px; }
|
||||
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Judul</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Pengumpulan</th> {{-- 🔥 baru --}}
|
||||
<th>Status</th> {{-- 🔥 baru --}}
|
||||
<th>File</th>
|
||||
<th>Deadline</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
/* CARD */
|
||||
.section-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; }
|
||||
.card-toolbar { display:flex; align-items:center; justify-content:space-between; padding:16px 22px; border-bottom:1px solid var(--border); background:var(--surface-secondary); }
|
||||
.toolbar-title { font-size:13.5px; font-weight:600; color:var(--text-primary); }
|
||||
.toolbar-badge { background:var(--primary-light); color:var(--primary); border:1px solid var(--primary-border); border-radius:999px; font-size:11.5px; font-weight:600; padding:2px 10px; margin-left:8px; }
|
||||
|
||||
<tbody>
|
||||
@forelse($tugas as $t)
|
||||
<tr>
|
||||
<td class="fw-semibold">{{ $t->judul }}</td>
|
||||
/* TABLE */
|
||||
.table-responsive { overflow-x:auto; }
|
||||
table.tugas-table { width:100%; border-collapse:collapse; min-width:780px; }
|
||||
.tugas-table thead tr { background:var(--surface-secondary); border-bottom:1px solid var(--border); }
|
||||
.tugas-table thead th { padding:12px 16px; font-size:11px; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.7px; text-align:left; white-space:nowrap; }
|
||||
.tugas-table thead th.center { text-align:center; }
|
||||
.tugas-table tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
||||
.tugas-table tbody tr:last-child { border-bottom:none; }
|
||||
.tugas-table tbody tr:hover { background:#f8faff; }
|
||||
.tugas-table tbody td { padding:15px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
||||
.tugas-table tbody td.center { text-align:center; }
|
||||
|
||||
<td>{{ $t->kelas->nama_kelas ?? '-' }}</td>
|
||||
.judul-cell { font-weight:600; color:var(--text-primary); }
|
||||
|
||||
<td>{{ $t->mapel->nama_mapel ?? '-' }}</td>
|
||||
.badge-kelas { background:var(--primary-light); color:var(--primary); border:1px solid var(--primary-border); border-radius:999px; font-size:11.5px; font-weight:600; padding:3px 11px; display:inline-flex; }
|
||||
.badge-mapel { background:var(--surface-secondary); color:var(--text-secondary); border:1px solid var(--border); border-radius:999px; font-size:11.5px; font-weight:600; padding:3px 11px; display:inline-flex; }
|
||||
|
||||
{{-- 🔥 JUMLAH PENGUMPULAN --}}
|
||||
<td>
|
||||
<span class="badge bg-primary">
|
||||
{{ $t->pengumpulan_count }} / {{ $t->total_siswa }}
|
||||
</span>
|
||||
</td>
|
||||
.badge-kumpul { background:var(--primary-light); color:var(--primary); border:1px solid var(--primary-border); border-radius:var(--radius-sm); font-size:12px; font-weight:700; padding:3px 10px; display:inline-flex; align-items:center; }
|
||||
.badge-aktif { background:var(--success-light); color:var(--success); border:1px solid var(--success-border); border-radius:999px; font-size:12px; font-weight:600; padding:4px 10px; display:inline-flex; align-items:center; gap:4px; }
|
||||
.badge-aktif::before { content:''; width:6px; height:6px; border-radius:50%; background:var(--success); }
|
||||
.badge-lewat { background:var(--danger-light); color:var(--danger); border:1px solid var(--danger-border); border-radius:999px; font-size:12px; font-weight:600; padding:4px 10px; display:inline-flex; align-items:center; gap:4px; }
|
||||
.badge-lewat::before { content:''; width:6px; height:6px; border-radius:50%; background:var(--danger); }
|
||||
|
||||
{{-- 🔥 STATUS DEADLINE --}}
|
||||
<td>
|
||||
@if(now()->gt($t->deadline))
|
||||
<span class="badge bg-danger">Lewat</span>
|
||||
@else
|
||||
<span class="badge bg-success">Aktif</span>
|
||||
@endif
|
||||
</td>
|
||||
.deadline-text { font-size:12.5px; color:var(--text-secondary); white-space:nowrap; }
|
||||
|
||||
{{-- FILE --}}
|
||||
<td>
|
||||
@if($t->file)
|
||||
<a href="{{ route('guru.tugas.download', $t->id) }}" class="btn btn-sm btn-success">
|
||||
Download
|
||||
</a>
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
/* ACTION GROUP */
|
||||
.action-group { display:flex; align-items:center; justify-content:center; gap:5px; flex-wrap:wrap; }
|
||||
.btn-act { display:inline-flex; align-items:center; gap:4px; padding:5px 11px; font-size:12px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; border-radius:var(--radius-sm); text-decoration:none; border:1px solid transparent; cursor:pointer; transition:all 0.15s; white-space:nowrap; }
|
||||
.btn-act svg { width:12px; height:12px; }
|
||||
.btn-pengumpulan { background:var(--primary-light); color:var(--primary); border-color:var(--primary-border); }
|
||||
.btn-pengumpulan:hover { background:#dbeafe; color:var(--primary); text-decoration:none; }
|
||||
.btn-detail { background:var(--info-light); color:var(--info); border-color:var(--info-border); }
|
||||
.btn-detail:hover { background:#e0f2fe; color:var(--info); text-decoration:none; }
|
||||
.btn-edit-act { background:var(--warning-light); color:var(--warning); border-color:var(--warning-border); }
|
||||
.btn-edit-act:hover { background:#fef3c7; color:var(--warning); text-decoration:none; }
|
||||
|
||||
{{-- DEADLINE --}}
|
||||
<td>
|
||||
<span class="badge bg-light text-dark">
|
||||
{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}
|
||||
</span>
|
||||
</td>
|
||||
/* EMPTY */
|
||||
.empty-state { padding:64px 24px; text-align:center; }
|
||||
.empty-icon { width:60px; height:60px; background:var(--surface-secondary); border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; margin:0 auto 14px; }
|
||||
.empty-icon svg { width:26px; height:26px; color:var(--text-muted); }
|
||||
.empty-title { font-size:15px; font-weight:600; color:var(--text-primary); margin-bottom:4px; }
|
||||
.empty-desc { font-size:13px; color:var(--text-muted); margin-bottom:20px; }
|
||||
.btn-empty { display:inline-flex; align-items:center; gap:7px; padding:10px 20px; font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif; background:var(--primary); color:#fff; border:none; border-radius:var(--radius-md); text-decoration:none; }
|
||||
.btn-empty:hover { background:var(--primary-dark); color:#fff; text-decoration:none; }
|
||||
|
||||
{{-- AKSI --}}
|
||||
<td class="text-center">
|
||||
.table-footer { display:flex; align-items:center; justify-content:space-between; padding:13px 22px; border-top:1px solid var(--border); background:var(--surface-secondary); font-size:12.5px; color:var(--text-muted); }
|
||||
|
||||
<a href="{{ route('guru.tugas.pengumpulan', $t->id) }}"
|
||||
class="btn btn-sm btn-primary mb-1">
|
||||
Pengumpulan
|
||||
</a>
|
||||
/* RATA-RATA SECTION */
|
||||
.btn-hitung-rata {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 10px 22px; border-radius: var(--radius-md); border: none;
|
||||
background: var(--success); color: #fff;
|
||||
font-size: 13.5px; font-weight: 600;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .18s;
|
||||
box-shadow: 0 2px 8px rgba(22,163,74,.25);
|
||||
margin-top: 24px;
|
||||
}
|
||||
.btn-hitung-rata:hover { background: #15803d; transform: translateY(-1px); }
|
||||
.btn-hitung-rata svg { width: 15px; height: 15px; }
|
||||
|
||||
<a href="{{ route('guru.tugas.show', $t->id) }}"
|
||||
class="btn btn-sm btn-info mb-1">
|
||||
Detail
|
||||
</a>
|
||||
.rata-section { margin-top: 20px; animation: fadeUp 0.3s ease both; }
|
||||
|
||||
<a href="{{ route('guru.tugas.edit', $t->id) }}"
|
||||
class="btn btn-sm btn-warning">
|
||||
Edit
|
||||
</a>
|
||||
.rata-card {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl); box-shadow: var(--shadow-md); overflow: hidden;
|
||||
}
|
||||
.rata-card-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 16px 22px; border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
}
|
||||
.rata-toolbar-left { display: flex; align-items: center; gap: 10px; }
|
||||
.rata-toolbar-icon {
|
||||
width: 34px; height: 34px; border-radius: 9px;
|
||||
background: var(--success); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.rata-toolbar-icon svg { width: 16px; height: 16px; }
|
||||
.rata-toolbar-title { font-size: 13.5px; font-weight: 700; color: #166534; margin: 0; }
|
||||
.rata-toolbar-sub { font-size: 11.5px; color: #15803d; margin: 2px 0 0; }
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" class="text-center text-muted">
|
||||
Belum ada tugas
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
table.rata-table { width: 100%; border-collapse: collapse; min-width: 600px; }
|
||||
.rata-table thead tr {
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
border-bottom: 2px solid #bbf7d0;
|
||||
}
|
||||
.rata-table thead th {
|
||||
padding: 12px 16px; font-size: 11px; font-weight: 700;
|
||||
color: #166534; text-transform: uppercase; letter-spacing: .7px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.rata-table thead th.center { text-align: center; }
|
||||
.rata-table tbody tr { border-bottom: 1px solid var(--border); transition: background .15s; }
|
||||
.rata-table tbody tr:last-child { border-bottom: none; }
|
||||
.rata-table tbody tr:hover { background: #f0fdf4; }
|
||||
.rata-table tbody td { padding: 14px 16px; font-size: 13.5px; color: var(--text-primary); vertical-align: middle; }
|
||||
.rata-table tbody td.center { text-align: center; }
|
||||
|
||||
</table>
|
||||
.rata-avatar-row { display: flex; align-items: center; gap: 10px; }
|
||||
.rata-avatar {
|
||||
width: 36px; height: 36px; border-radius: 50%;
|
||||
background: var(--success-light); border: 2px solid #bbf7d0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 13px; font-weight: 800; color: var(--success); flex-shrink: 0;
|
||||
}
|
||||
.rata-nama { font-weight: 700; color: var(--text-primary); font-size: 13.5px; }
|
||||
|
||||
.chip-nilai {
|
||||
display: inline-flex; padding: 5px 16px;
|
||||
border-radius: 999px; font-size: 14px; font-weight: 800;
|
||||
}
|
||||
.chip-nilai.a { background: #dcfce7; color: #166534; }
|
||||
.chip-nilai.b { background: var(--primary-light); color: var(--primary); }
|
||||
.chip-nilai.c { background: var(--warning-light); color: var(--warning); }
|
||||
.chip-nilai.d { background: var(--danger-light); color: var(--danger); }
|
||||
|
||||
.chip-predikat {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 4px 12px; border-radius: 999px; font-size: 11.5px; font-weight: 700;
|
||||
}
|
||||
.chip-predikat.a { background: #dcfce7; color: #166534; }
|
||||
.chip-predikat.b { background: var(--primary-light); color: var(--primary); }
|
||||
.chip-predikat.c { background: var(--warning-light); color: var(--warning); }
|
||||
.chip-predikat.d { background: var(--danger-light); color: var(--danger); }
|
||||
|
||||
.rata-footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 13px 22px; border-top: 1px solid var(--border);
|
||||
background: var(--surface-secondary); font-size: 13px; color: var(--text-muted);
|
||||
}
|
||||
.rata-footer-nilai { font-size: 16px; font-weight: 800; color: var(--text-primary); }
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-4 py-2 page-wrapper">
|
||||
|
||||
<div class="page-header">
|
||||
<div class="page-header-left">
|
||||
<div class="page-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="page-title">Manajemen Tugas</h4>
|
||||
<p class="page-subtitle">Kelola dan pantau tugas yang diberikan kepada siswa</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a href="{{ route('guru.tugas.create') }}" class="btn-add">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
|
||||
Tambah Tugas
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="section-card">
|
||||
<div class="card-toolbar">
|
||||
<span class="toolbar-title">Daftar Tugas<span class="toolbar-badge">{{ $tugas->count() }} tugas</span></span>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="tugas-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Judul Tugas</th>
|
||||
<th>Kelas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th class="center">Pengumpulan</th>
|
||||
<th class="center">Status</th>
|
||||
<th class="center">File</th>
|
||||
<th>Deadline</th>
|
||||
<th class="center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($tugas as $t)
|
||||
<tr>
|
||||
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
||||
<td><span class="badge-kelas">{{ $t->kelas->nama_kelas ?? '—' }}</span></td>
|
||||
<td><span class="badge-mapel">{{ $t->mapel->nama_mapel ?? '—' }}</span></td>
|
||||
<td class="center">
|
||||
<span class="badge-kumpul">{{ $t->pengumpulan_count }} / {{ $t->total_siswa }}</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
@if(now()->gt($t->deadline))
|
||||
<span class="badge-lewat">Lewat</span>
|
||||
@else
|
||||
<span class="badge-aktif">Aktif</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="center">
|
||||
@if($t->file)
|
||||
<a href="{{ route('guru.tugas.download', $t->id) }}" class="btn-act btn-detail">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
|
||||
Unduh
|
||||
</a>
|
||||
@else
|
||||
<span style="font-size:12px;color:var(--text-muted);">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td><span class="deadline-text">{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}</span></td>
|
||||
<td>
|
||||
<div class="action-group">
|
||||
<a href="{{ route('guru.tugas.pengumpulan', $t->id) }}" class="btn-act btn-pengumpulan">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
Kumpulan
|
||||
</a>
|
||||
<a href="{{ route('guru.tugas.show', $t->id) }}" class="btn-act btn-detail">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
|
||||
Detail
|
||||
</a>
|
||||
<a href="{{ route('guru.tugas.edit', $t->id) }}" class="btn-act btn-edit-act">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536M9 13l6.586-6.586a2 2 0 012.828 2.828L11.828 15.828a2 2 0 01-1.414.586H8v-2.414a2 2 0 01.586-1.414z"/></svg>
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum ada tugas</div>
|
||||
<div class="empty-desc">Buat tugas pertama untuk mulai memantau pengumpulan siswa.</div>
|
||||
<a href="{{ route('guru.tugas.create') }}" class="btn-empty">Buat Tugas Pertama</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if($tugas->count())
|
||||
<div class="table-footer">
|
||||
<span>{{ $tugas->count() }} tugas ditemukan</span>
|
||||
<span>{{ now()->format('d M Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- TOMBOL HITUNG --}}
|
||||
<div style="text-align:right">
|
||||
<button class="btn-hitung-rata" onclick="tampilRataRata()">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
Hitung Rata-Rata Nilai Siswa
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- TABEL RATA-RATA --}}
|
||||
<div id="rata-section" class="rata-section" style="display:none">
|
||||
<div class="rata-card">
|
||||
<div class="rata-card-toolbar">
|
||||
<div class="rata-toolbar-left">
|
||||
<div class="rata-toolbar-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="rata-toolbar-title">Rekap Rata-Rata Nilai per Siswa</p>
|
||||
<p class="rata-toolbar-sub">Dihitung dari semua tugas yang sudah dinilai guru</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="rata-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:36px">#</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th class="center">Jumlah Tugas Dinilai</th>
|
||||
<th class="center">Rata-Rata Nilai</th>
|
||||
<th class="center">Predikat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rekapNilai as $i => $r)
|
||||
<tr>
|
||||
<td style="color:var(--text-muted);font-size:12px">{{ $i + 1 }}</td>
|
||||
<td>
|
||||
<div class="rata-avatar-row">
|
||||
<div class="rata-avatar">{{ $r['inisial'] }}</div>
|
||||
<span class="rata-nama">{{ $r['nama'] }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="center" style="color:var(--text-secondary)">
|
||||
{{ $r['jumlah_tugas'] }} tugas
|
||||
</td>
|
||||
<td class="center">
|
||||
@php
|
||||
$n = $r['rata_rata'];
|
||||
$cls = $n >= 88 ? 'a' : ($n >= 75 ? 'b' : ($n >= 60 ? 'c' : 'd'));
|
||||
@endphp
|
||||
<span class="chip-nilai {{ $cls }}">{{ $n }}</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
@php
|
||||
$predikat = $n >= 88 ? 'A — Sangat Baik'
|
||||
: ($n >= 75 ? 'B — Baik'
|
||||
: ($n >= 60 ? 'C — Cukup' : 'D — Kurang'));
|
||||
@endphp
|
||||
<span class="chip-predikat {{ $cls }}">{{ $predikat }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" style="text-align:center;padding:40px;color:var(--text-muted);font-size:13px">
|
||||
Belum ada siswa yang mendapatkan nilai
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if($rekapNilai->count())
|
||||
<div class="rata-footer">
|
||||
<span>{{ $rekapNilai->count() }} siswa terhitung</span>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<span>Rata-rata kelas:</span>
|
||||
<span class="rata-footer-nilai">
|
||||
{{ round($rekapNilai->avg('rata_rata'), 1) }} / 100
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function tampilRataRata() {
|
||||
const sec = document.getElementById('rata-section');
|
||||
sec.style.display = 'block';
|
||||
sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,78 +1,533 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h4>Pengumpulan Tugas: {{ $tugas->judul }}</h4>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<table class="table table-bordered">
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama Siswa</th>
|
||||
<th>File</th>
|
||||
<th>Waktu Kumpul</th>
|
||||
<th>Status</th>
|
||||
<th>Nilai</th>
|
||||
<th>Komentar</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<tbody>
|
||||
@foreach($tugas->pengumpulan as $p)
|
||||
<tr>
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
<td>{{ $p->siswa->nama ?? '-' }}</td>
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 24px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 4px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
<td>
|
||||
<a href="{{ asset('storage/' . $p->file) }}" target="_blank">
|
||||
Download
|
||||
</a>
|
||||
</td>
|
||||
/* ── HEADER CARD ── */
|
||||
.sw-header-card {
|
||||
background: linear-gradient(135deg, #1E3A6E 0%, #3730A3 55%, #4F46E5 100%);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px 28px;
|
||||
display: flex; align-items: center; gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.sw-header-card::before {
|
||||
content: ''; position: absolute;
|
||||
right: -40px; top: -40px;
|
||||
width: 180px; height: 180px; border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,.08);
|
||||
}
|
||||
.sw-header-icon {
|
||||
width: 52px; height: 52px; border-radius: 14px;
|
||||
background: rgba(255,255,255,.15);
|
||||
border: 1px solid rgba(255,255,255,.2);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.sw-header-title { font-size: 19px; font-weight: 800; color: #fff; margin: 0 0 6px; line-height: 1.3; }
|
||||
.sw-header-chips { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
.sw-header-chip {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2);
|
||||
border-radius: 99px; padding: 4px 12px;
|
||||
font-size: 11.5px; font-weight: 600; color: rgba(255,255,255,.9);
|
||||
}
|
||||
|
||||
<td>
|
||||
{{ \Carbon\Carbon::parse($p->created_at)->format('d M Y H:i') }}
|
||||
</td>
|
||||
/* ── SUMMARY STATS ── */
|
||||
.sw-stats { display: flex; gap: 14px; margin-bottom: 24px; flex-wrap: wrap; }
|
||||
.sw-stat {
|
||||
flex: 1; min-width: 130px;
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border);
|
||||
padding: 16px 18px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.04);
|
||||
}
|
||||
.sw-stat-icon {
|
||||
width: 40px; height: 40px; border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||
}
|
||||
.sw-stat-icon.primary { background: var(--primary-light); color: var(--primary); }
|
||||
.sw-stat-icon.success { background: var(--success-light); color: var(--success); }
|
||||
.sw-stat-icon.warning { background: var(--warning-light); color: var(--warning); }
|
||||
.sw-stat-val { font-size: 22px; font-weight: 800; color: var(--text-dark); line-height: 1; }
|
||||
.sw-stat-lbl { font-size: 11.5px; font-weight: 600; color: var(--text-soft); margin-top: 3px; }
|
||||
|
||||
<td>
|
||||
@if($p->status == 'telat')
|
||||
<span class="badge bg-warning text-dark">Telat</span>
|
||||
@else
|
||||
<span class="badge bg-success">Tepat Waktu</span>
|
||||
@endif
|
||||
</td>
|
||||
/* ── MAIN CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
.sw-card-hd {
|
||||
padding: 16px 22px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.sw-card-hd-left { display: flex; align-items: center; gap: 10px; }
|
||||
.sw-card-hd-icon {
|
||||
width: 34px; height: 34px; border-radius: 9px;
|
||||
background: var(--primary); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.sw-card-hd h6 { font-size: 14px; font-weight: 700; color: #3730A3; margin: 0; }
|
||||
.sw-card-hd p { font-size: 11.5px; color: #6D6AA4; margin: 2px 0 0; }
|
||||
|
||||
<!-- 🔥 FORM NILAI LANGSUNG -->
|
||||
<form method="POST" action="{{ route('guru.tugas.nilai', $p->id) }}">
|
||||
@csrf
|
||||
/* ── TABLE ── */
|
||||
.sw-table-wrap { overflow-x: auto; }
|
||||
table.sw-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
||||
|
||||
<td>
|
||||
<input type="number" name="nilai"
|
||||
value="{{ $p->nilai }}"
|
||||
class="form-control"
|
||||
style="width:80px;">
|
||||
</td>
|
||||
table.sw-table thead tr {
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
border-bottom: 2px solid #DDD6FE;
|
||||
}
|
||||
table.sw-table thead th {
|
||||
padding: 12px 16px; font-weight: 700;
|
||||
font-size: 11.5px; color: #5B21B6;
|
||||
text-transform: uppercase; letter-spacing: .6px;
|
||||
white-space: nowrap; border: none;
|
||||
}
|
||||
table.sw-table tbody tr { border-bottom: 1px solid var(--border); transition: background .15s; }
|
||||
table.sw-table tbody tr:last-child { border-bottom: none; }
|
||||
table.sw-table tbody tr:hover { background: #FAFAFF; }
|
||||
table.sw-table td { padding: 14px 16px; border: none; vertical-align: middle; color: var(--text-mid); }
|
||||
|
||||
<td>
|
||||
<input type="text" name="komentar"
|
||||
value="{{ $p->komentar }}"
|
||||
class="form-control">
|
||||
</td>
|
||||
/* ── AVATAR ── */
|
||||
.sw-avatar-row { display: flex; align-items: center; gap: 10px; }
|
||||
.sw-avatar {
|
||||
width: 36px; height: 36px; border-radius: 50%;
|
||||
background: var(--primary-light); border: 2px solid #C7D2FE;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 13px; font-weight: 800; color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sw-avatar-name { font-weight: 700; color: var(--text-dark); font-size: 13.5px; }
|
||||
|
||||
<td>
|
||||
<button class="btn btn-sm btn-success">
|
||||
Simpan
|
||||
</button>
|
||||
</td>
|
||||
/* ── DOWNLOAD LINK ── */
|
||||
.sw-dl-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--primary-light); border: 1px solid #C7D2FE;
|
||||
border-radius: 8px; padding: 6px 12px;
|
||||
font-size: 12px; font-weight: 600; color: var(--primary);
|
||||
text-decoration: none; transition: all .15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw-dl-btn:hover { background: var(--primary); color: #fff; }
|
||||
|
||||
</form>
|
||||
/* ── TIME ── */
|
||||
.sw-time { font-size: 12.5px; color: var(--text-soft); white-space: nowrap; }
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
/* ── BADGES ── */
|
||||
.sw-badge {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 4px 10px; border-radius: 99px;
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
}
|
||||
.sw-badge-success { background: var(--success-light); color: var(--success); }
|
||||
.sw-badge-warning { background: var(--warning-light); color: var(--warning); }
|
||||
.sw-badge span { width: 6px; height: 6px; border-radius: 50%; }
|
||||
.sw-badge-success span { background: var(--success); }
|
||||
.sw-badge-warning span { background: var(--warning); }
|
||||
|
||||
</table>
|
||||
/* ── INLINE FORM INPUTS ── */
|
||||
.sw-nilai-input {
|
||||
width: 72px; padding: 7px 10px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: 8px; font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; text-align: center;
|
||||
transition: all .15s;
|
||||
}
|
||||
.sw-nilai-input:focus { border-color: var(--primary); background: #fff; box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
|
||||
|
||||
.sw-komentar-input {
|
||||
width: 100%; min-width: 160px; padding: 7px 12px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: 8px; font-size: 13px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-komentar-input:focus { border-color: var(--primary); background: #fff; box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
|
||||
|
||||
/* ── SAVE BUTTON ── */
|
||||
.sw-save-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 7px 14px; border-radius: 8px; border: none;
|
||||
background: var(--success); color: #fff;
|
||||
font-size: 12.5px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .18s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sw-save-btn:hover { background: #047857; transform: translateY(-1px); box-shadow: 0 4px 10px rgba(5,150,105,.3); }
|
||||
|
||||
/* ── EMPTY ── */
|
||||
.sw-empty { padding: 50px 20px; text-align: center; color: var(--text-soft); }
|
||||
.sw-empty-icon { font-size: 36px; opacity: .3; margin-bottom: 10px; }
|
||||
.sw-empty p { font-size: 14px; margin: 0; }
|
||||
|
||||
@media (max-width: 768px) { .sw-page { padding: 14px; } }
|
||||
|
||||
.btn-hitung-rata {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 10px 22px; border-radius: 10px; border: none;
|
||||
background: #059669; color: #fff;
|
||||
font-size: 13.5px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .18s;
|
||||
}
|
||||
.btn-hitung-rata:hover { background: #047857; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(5,150,105,.3); }
|
||||
.badge-predikat {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 4px 12px; border-radius: 99px;
|
||||
font-size: 11.5px; font-weight: 700;
|
||||
}
|
||||
.badge-predikat.a { background: #DCFCE7; color: #166534; }
|
||||
.badge-predikat.b { background: #EEF2FF; color: #3730A3; }
|
||||
.badge-predikat.c { background: #FFFBEB; color: #92400E; }
|
||||
.badge-predikat.d { background: #FEF2F2; color: #991B1B; }
|
||||
.nilai-chip {
|
||||
display: inline-flex; padding: 5px 16px;
|
||||
border-radius: 99px; font-size: 15px; font-weight: 800;
|
||||
}
|
||||
.nilai-chip.a { background: #DCFCE7; color: #166534; }
|
||||
.nilai-chip.b { background: #EEF2FF; color: #3730A3; }
|
||||
.nilai-chip.c { background: #FFFBEB; color: #92400E; }
|
||||
.nilai-chip.d { background: #FEF2F2; color: #991B1B; }
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<h3>Pengumpulan Tugas</h3>
|
||||
<p>Nilai dan berikan komentar untuk setiap siswa</p>
|
||||
</div>
|
||||
|
||||
{{-- HEADER CARD --}}
|
||||
<div class="sw-header-card">
|
||||
<div class="sw-header-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
</div>
|
||||
<div style="position:relative;z-index:1;">
|
||||
<div class="sw-header-title">{{ $tugas->judul }}</div>
|
||||
<div class="sw-header-chips">
|
||||
<div class="sw-header-chip">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
|
||||
{{ $tugas->pengumpulan->count() }} Pengumpulan
|
||||
</div>
|
||||
<div class="sw-header-chip">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
{{ $tugas->pengumpulan->whereNotNull('nilai')->count() }} Sudah Dinilai
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATS --}}
|
||||
<div class="sw-stats">
|
||||
<div class="sw-stat">
|
||||
<div class="sw-stat-icon primary">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $tugas->pengumpulan->count() }}</div>
|
||||
<div class="sw-stat-lbl">Total Kumpul</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-stat">
|
||||
<div class="sw-stat-icon success">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $tugas->pengumpulan->where('status', '!=', 'telat')->count() }}</div>
|
||||
<div class="sw-stat-lbl">Tepat Waktu</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-stat">
|
||||
<div class="sw-stat-icon warning">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $tugas->pengumpulan->where('status', 'telat')->count() }}</div>
|
||||
<div class="sw-stat-lbl">Telat</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-stat">
|
||||
<div class="sw-stat-icon primary">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-val">{{ $tugas->pengumpulan->whereNotNull('nilai')->count() }}</div>
|
||||
<div class="sw-stat-lbl">Sudah Dinilai</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- TABLE CARD --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-left">
|
||||
<div class="sw-card-hd-icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h6>Daftar Pengumpulan</h6>
|
||||
<p>Isi nilai dan komentar lalu klik Simpan</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-table-wrap">
|
||||
<table class="sw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:36px">#</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th>File</th>
|
||||
<th>Waktu Kumpul</th>
|
||||
<th class="text-center">Status</th>
|
||||
<th class="text-center" style="width:90px">Nilai</th>
|
||||
<th>Komentar</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($tugas->pengumpulan as $p)
|
||||
<tr>
|
||||
<td style="color:var(--text-soft);font-size:12px;">{{ $loop->iteration }}</td>
|
||||
|
||||
<td>
|
||||
<div class="sw-avatar-row">
|
||||
<div class="sw-avatar">
|
||||
{{ strtoupper(substr($p->siswa->nama ?? 'S', 0, 1)) }}
|
||||
</div>
|
||||
<div class="sw-avatar-name">{{ $p->siswa->nama ?? '-' }}</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ asset('storage/' . $p->file) }}" target="_blank" class="sw-dl-btn">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||||
Download
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="sw-time">
|
||||
{{ \Carbon\Carbon::parse($p->created_at)->format('d M Y') }}<br>
|
||||
<span style="font-size:11.5px">{{ \Carbon\Carbon::parse($p->created_at)->format('H:i') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style="text-align:center">
|
||||
@if($p->status == 'telat')
|
||||
<span class="sw-badge sw-badge-warning"><span></span>Telat</span>
|
||||
@else
|
||||
<span class="sw-badge sw-badge-success"><span></span>Tepat Waktu</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
{{-- INLINE FORM --}}
|
||||
<form method="POST" action="{{ route('guru.tugas.nilai', $p->id) }}" style="display:contents">
|
||||
@csrf
|
||||
|
||||
<td style="text-align:center">
|
||||
<input type="number" name="nilai"
|
||||
value="{{ $p->nilai }}"
|
||||
class="sw-nilai-input"
|
||||
min="0" max="100"
|
||||
placeholder="—">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="text" name="komentar"
|
||||
value="{{ $p->komentar }}"
|
||||
class="sw-komentar-input"
|
||||
placeholder="Tambah komentar...">
|
||||
</td>
|
||||
|
||||
<td style="text-align:center">
|
||||
<button type="submit" class="sw-save-btn">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Simpan
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</form>
|
||||
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8">
|
||||
<div class="sw-empty">
|
||||
<div class="sw-empty-icon">📭</div>
|
||||
<p>Belum ada siswa yang mengumpulkan tugas ini</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{{-- TOMBOL HITUNG RATA-RATA --}}
|
||||
<!-- <div style="margin-top:20px;text-align:right">
|
||||
<button onclick="hitungRataRata()" class="btn-hitung-rata">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
Hitung Rata-Rata Nilai
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
{{-- TABEL RATA-RATA --}}
|
||||
<div id="rata-section" style="display:none;margin-top:20px">
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-left">
|
||||
<div class="sw-card-hd-icon" style="background:#059669">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M18 20V10M12 20V4M6 20v-6"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h6 style="color:#065F46">Rekap Nilai Rata-Rata Siswa</h6>
|
||||
<p>Rata-rata dihitung dari semua tugas yang sudah dinilai guru</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-table-wrap">
|
||||
<table class="sw-table">
|
||||
<thead style="background:linear-gradient(135deg,#F0FDF4,#DCFCE7)">
|
||||
<tr>
|
||||
<th style="color:#166534">#</th>
|
||||
<th style="color:#166534">Nama Siswa</th>
|
||||
<th style="color:#166534;text-align:center">Tugas Dikumpul</th>
|
||||
<th style="color:#166534;text-align:center">Tugas Dinilai</th>
|
||||
<th style="color:#166634;text-align:center">Rata-Rata Nilai</th>
|
||||
<th style="color:#166534;text-align:center">Predikat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="rata-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:13px 22px;border-top:1px solid var(--border);background:var(--neutral-light);font-size:13px;color:var(--text-soft)">
|
||||
<span>Rata-rata keseluruhan kelas</span>
|
||||
<strong id="rata-kelas" style="font-size:16px;color:var(--text-dark)">—</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const pengumpulanData = @json($pengumpulanJson);
|
||||
|
||||
function hitungRataRata() {
|
||||
const tbody = document.getElementById('rata-tbody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
let totalKelas = 0;
|
||||
let jumlahDinilai = 0;
|
||||
|
||||
pengumpulanData.forEach((p, i) => {
|
||||
const nilai = p.nilai !== null ? parseFloat(p.nilai) : null;
|
||||
const avgText = nilai !== null ? Math.round(nilai) : '—';
|
||||
const cls = nilai !== null ? getKelas(nilai) : '';
|
||||
|
||||
if (nilai !== null) {
|
||||
totalKelas += nilai;
|
||||
jumlahDinilai++;
|
||||
}
|
||||
|
||||
tbody.innerHTML += `
|
||||
<tr>
|
||||
<td style="color:var(--text-soft);font-size:12px">${i+1}</td>
|
||||
<td>
|
||||
<div class="sw-avatar-row">
|
||||
<div class="sw-avatar">${p.inisial}</div>
|
||||
<span style="font-weight:700">${p.nama}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style="text-align:center">1 tugas</td>
|
||||
<td style="text-align:center">
|
||||
${nilai !== null
|
||||
? '<span style="color:var(--success);font-weight:600">✔ Sudah dinilai</span>'
|
||||
: '<span style="color:var(--text-soft)">Belum dinilai</span>'}
|
||||
</td>
|
||||
<td style="text-align:center">
|
||||
${nilai !== null
|
||||
? `<span class="nilai-chip ${cls}">${avgText}</span>`
|
||||
: '<span style="color:var(--text-muted)">—</span>'}
|
||||
</td>
|
||||
<td style="text-align:center">
|
||||
${nilai !== null
|
||||
? `<span class="badge-predikat ${cls}">${getPredikat(nilai)}</span>`
|
||||
: '—'}
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
const rataKelas = jumlahDinilai > 0
|
||||
? Math.round(totalKelas / jumlahDinilai)
|
||||
: '—';
|
||||
|
||||
document.getElementById('rata-kelas').textContent =
|
||||
jumlahDinilai > 0 ? rataKelas + ' / 100' : 'Belum ada nilai';
|
||||
|
||||
document.getElementById('rata-section').style.display = 'block';
|
||||
document.getElementById('rata-section').scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
function getKelas(n) {
|
||||
if (n >= 88) return 'a';
|
||||
if (n >= 75) return 'b';
|
||||
if (n >= 60) return 'c';
|
||||
return 'd';
|
||||
}
|
||||
|
||||
function getPredikat(n) {
|
||||
if (n >= 88) return 'A — Sangat Baik';
|
||||
if (n >= 75) return 'B — Baik';
|
||||
if (n >= 60) return 'C — Cukup';
|
||||
return 'D — Kurang';
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
|
|
@ -1,23 +1,359 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h4>Detail Tugas</h4>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<p><b>Judul:</b> {{ $tugas->judul }}</p>
|
||||
<p><b>Kelas:</b> {{ $tugas->kelas->nama_kelas }}</p>
|
||||
<p><b>Mapel:</b> {{ $tugas->mapel->nama_mapel }}</p>
|
||||
<p><b>Deskripsi:</b> {{ $tugas->deskripsi }}</p>
|
||||
<p><b>Deadline:</b> {{ $tugas->deadline }}</p>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
<a href="{{ route('guru.tugas.index') }}" class="btn btn-secondary">Kembali</a>
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 24px; display: flex; align-items: center; gap: 12px; }
|
||||
.sw-back-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 36px; height: 36px;
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md); color: var(--text-mid);
|
||||
text-decoration: none; transition: all .15s; flex-shrink: 0;
|
||||
}
|
||||
.sw-back-btn:hover { background: var(--primary-light); border-color: #C7D2FE; color: var(--primary); }
|
||||
.sw-topbar-text h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 2px; letter-spacing: -.3px; }
|
||||
.sw-topbar-text p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
/* ── LAYOUT ── */
|
||||
.sw-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 320px;
|
||||
gap: 22px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── BANNER ── */
|
||||
.sw-card-banner {
|
||||
height: 6px;
|
||||
background: linear-gradient(90deg, #4F46E5, #7C3AED, #059669);
|
||||
}
|
||||
|
||||
/* ── CARD HEADER ── */
|
||||
.sw-card-hd {
|
||||
padding: 22px 26px 18px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
}
|
||||
.sw-card-hd-top {
|
||||
display: flex; align-items: flex-start; gap: 14px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.sw-card-hd-icon {
|
||||
width: 50px; height: 50px; border-radius: 13px;
|
||||
background: var(--primary); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; box-shadow: 0 4px 12px rgba(79,70,229,.3);
|
||||
}
|
||||
.sw-card-hd-title {
|
||||
font-size: 20px; font-weight: 800;
|
||||
color: var(--text-dark); margin: 0 0 8px;
|
||||
letter-spacing: -.3px; line-height: 1.3;
|
||||
}
|
||||
.sw-chip-row { display: flex; gap: 7px; flex-wrap: wrap; }
|
||||
.sw-chip {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
border-radius: 99px; padding: 4px 12px;
|
||||
font-size: 12px; font-weight: 600;
|
||||
}
|
||||
.sw-chip-primary { background: var(--primary-light); border: 1px solid #C7D2FE; color: var(--primary); }
|
||||
.sw-chip-info { background: var(--info-light); border: 1px solid #BAE6FD; color: var(--info); }
|
||||
|
||||
/* ── CARD BODY ── */
|
||||
.sw-card-bd { padding: 26px; }
|
||||
|
||||
/* ── SECTION ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 11.5px; font-weight: 700; color: var(--primary);
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #C7D2FE, transparent);
|
||||
}
|
||||
|
||||
/* ── INFO LIST ── */
|
||||
.sw-info-list { display: flex; flex-direction: column; gap: 0; margin-bottom: 26px; }
|
||||
.sw-info-row {
|
||||
display: flex; align-items: flex-start;
|
||||
padding: 13px 0; border-bottom: 1px solid var(--border);
|
||||
gap: 16px;
|
||||
}
|
||||
.sw-info-row:last-child { border-bottom: none; }
|
||||
.sw-info-label {
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
font-size: 12.5px; font-weight: 600; color: var(--text-soft);
|
||||
min-width: 110px; flex-shrink: 0; padding-top: 1px;
|
||||
}
|
||||
.sw-info-label svg { color: var(--primary); flex-shrink: 0; }
|
||||
.sw-info-val { font-size: 14px; font-weight: 600; color: var(--text-dark); flex: 1; }
|
||||
|
||||
/* ── DESCRIPTION BOX ── */
|
||||
.sw-desc-box {
|
||||
background: var(--neutral-light);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 16px 18px;
|
||||
font-size: 14px; color: var(--text-mid);
|
||||
line-height: 1.7; margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ── DEADLINE CARD ── */
|
||||
.sw-deadline-box {
|
||||
background: linear-gradient(135deg, #FFFBEB, #FEF3C7);
|
||||
border: 1px solid #FDE68A;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 16px 18px;
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
}
|
||||
.sw-deadline-icon {
|
||||
width: 44px; height: 44px; border-radius: 11px;
|
||||
background: var(--warning); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; box-shadow: 0 3px 10px rgba(217,119,6,.3);
|
||||
}
|
||||
.sw-deadline-label { font-size: 11.5px; font-weight: 600; color: #92400E; text-transform: uppercase; letter-spacing: .5px; }
|
||||
.sw-deadline-val { font-size: 16px; font-weight: 800; color: #78350F; margin-top: 3px; }
|
||||
|
||||
/* ── SIDEBAR ── */
|
||||
.sw-sidebar-card { padding: 20px; }
|
||||
.sw-sidebar-title {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 13px; font-weight: 700; color: var(--text-dark);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sw-sidebar-title svg { color: var(--primary); }
|
||||
|
||||
/* stat mini */
|
||||
.sw-mini-stats { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
|
||||
.sw-mini-stat {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 14px;
|
||||
background: var(--neutral-light); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.sw-mini-stat-left { display: flex; align-items: center; gap: 9px; font-size: 13px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-mini-stat-left svg { color: var(--text-soft); }
|
||||
.sw-mini-stat-val { font-size: 15px; font-weight: 800; color: var(--text-dark); }
|
||||
|
||||
/* divider */
|
||||
.sw-divider { height: 1px; background: var(--border); margin: 16px 0; }
|
||||
|
||||
/* action buttons */
|
||||
.sw-actions { display: flex; flex-direction: column; gap: 8px; }
|
||||
.sw-action-link {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 12px 14px; border-radius: var(--radius-md);
|
||||
text-decoration: none; font-size: 13.5px; font-weight: 600;
|
||||
transition: all .18s; border: 1px solid transparent;
|
||||
}
|
||||
.sw-action-link.primary { background: var(--primary-light); border-color: #C7D2FE; color: var(--primary); }
|
||||
.sw-action-link.primary:hover { background: var(--primary); color: #fff; }
|
||||
.sw-action-link.secondary { background: var(--neutral-light); border-color: var(--border); color: var(--text-mid); }
|
||||
.sw-action-link.secondary:hover { background: var(--surface); color: var(--text-dark); }
|
||||
.sw-action-link svg { flex-shrink: 0; }
|
||||
.sw-action-link svg.arrow { margin-left: auto; opacity: .45; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sw-layout { grid-template-columns: 1fr; }
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-card-bd, .sw-card-hd { padding: 18px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<a href="{{ route('guru.tugas.index') }}" class="sw-back-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
</a>
|
||||
<div class="sw-topbar-text">
|
||||
<h3>Detail Tugas</h3>
|
||||
<p>Informasi lengkap tugas yang diberikan</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-layout">
|
||||
|
||||
{{-- ── MAIN CARD ── --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-banner"></div>
|
||||
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-top">
|
||||
<div class="sw-card-hd-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-card-hd-title">{{ $tugas->judul }}</div>
|
||||
<div class="sw-chip-row">
|
||||
<span class="sw-chip sw-chip-primary">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
||||
{{ $tugas->kelas->nama_kelas }}
|
||||
</span>
|
||||
<span class="sw-chip sw-chip-info">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
{{ $tugas->mapel->nama_mapel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-bd">
|
||||
|
||||
{{-- INFO ROWS --}}
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Informasi Tugas</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="sw-info-list">
|
||||
<div class="sw-info-row">
|
||||
<div class="sw-info-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
Judul
|
||||
</div>
|
||||
<div class="sw-info-val">{{ $tugas->judul }}</div>
|
||||
</div>
|
||||
<div class="sw-info-row">
|
||||
<div class="sw-info-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
||||
Kelas
|
||||
</div>
|
||||
<div class="sw-info-val">{{ $tugas->kelas->nama_kelas }}</div>
|
||||
</div>
|
||||
<div class="sw-info-row">
|
||||
<div class="sw-info-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
Mapel
|
||||
</div>
|
||||
<div class="sw-info-val">{{ $tugas->mapel->nama_mapel }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- DESKRIPSI --}}
|
||||
<div class="sw-section" style="margin-top:4px;">
|
||||
<span class="sw-section-label">Deskripsi</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
<div class="sw-desc-box">
|
||||
{{ $tugas->deskripsi ?: 'Tidak ada deskripsi.' }}
|
||||
</div>
|
||||
|
||||
{{-- DEADLINE --}}
|
||||
<div class="sw-section" style="margin-top:24px;">
|
||||
<span class="sw-section-label">Batas Waktu</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
<div class="sw-deadline-box">
|
||||
<div class="sw-deadline-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-deadline-label">Deadline Pengumpulan</div>
|
||||
<div class="sw-deadline-val">{{ $tugas->deadline }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── SIDEBAR ── --}}
|
||||
<div>
|
||||
<div class="sw-card sw-sidebar-card">
|
||||
<div class="sw-sidebar-title">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
|
||||
Aksi
|
||||
</div>
|
||||
|
||||
<div class="sw-actions">
|
||||
<a href="{{ route('guru.tugas.index') }}" class="sw-action-link secondary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali ke Daftar
|
||||
<svg class="arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sw-divider"></div>
|
||||
|
||||
<div class="sw-sidebar-title">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
Ringkasan
|
||||
</div>
|
||||
|
||||
<div class="sw-mini-stats">
|
||||
<div class="sw-mini-stat">
|
||||
<div class="sw-mini-stat-left">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
||||
Kelas
|
||||
</div>
|
||||
<div class="sw-mini-stat-val">{{ $tugas->kelas->nama_kelas }}</div>
|
||||
</div>
|
||||
<div class="sw-mini-stat">
|
||||
<div class="sw-mini-stat-left">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
Mapel
|
||||
</div>
|
||||
<div class="sw-mini-stat-val" style="font-size:13px;">{{ $tugas->mapel->nama_mapel }}</div>
|
||||
</div>
|
||||
<div class="sw-mini-stat">
|
||||
<div class="sw-mini-stat-left">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
Deadline
|
||||
</div>
|
||||
<div class="sw-mini-stat-val" style="font-size:12.5px;">{{ $tugas->deadline }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,131 +1,534 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h2 class="mb-4 text-primary">📝 Buat Ujian + Soal</h2>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
@if($errors->any())
|
||||
<div class="alert alert-danger">{{ $errors->first() }}</div>
|
||||
@endif
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: 100vh; }
|
||||
|
||||
<form method="POST" action="{{ route('guru.ujian.store') }}">
|
||||
@csrf
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { display: flex; align-items: center; gap: 12px; margin-bottom: 26px; }
|
||||
.sw-back-btn {
|
||||
width: 36px; height: 36px;
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md); color: var(--text-mid);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
text-decoration: none; transition: all .15s; flex-shrink: 0;
|
||||
}
|
||||
.sw-back-btn:hover { background: var(--primary-light); border-color: #C7D2FE; color: var(--primary); }
|
||||
.sw-topbar-text h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 2px; letter-spacing: -.3px; }
|
||||
.sw-topbar-text p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
{{-- ================= UJIAN ================= --}}
|
||||
<h5 class="text-primary mb-3">📘 Data Ujian</h5>
|
||||
/* ── ALERTS ── */
|
||||
.sw-alert {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
border-radius: var(--radius-md); padding: 13px 16px;
|
||||
font-size: 13px; margin-bottom: 22px; font-weight: 500;
|
||||
}
|
||||
.sw-alert-success { background: var(--success-light); border: 1px solid #A7F3D0; color: var(--success); }
|
||||
.sw-alert-danger { background: var(--danger-light); border: 1px solid #FECACA; color: var(--danger); }
|
||||
.sw-alert svg { flex-shrink: 0; margin-top: 1px; }
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Judul</label>
|
||||
<input type="text" name="judul" class="form-control" required>
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sw-card-banner { height: 5px; }
|
||||
.sw-card-banner.purple { background: linear-gradient(90deg, #4F46E5, #7C3AED); }
|
||||
.sw-card-banner.green { background: linear-gradient(90deg, #059669, #10B981); }
|
||||
|
||||
.sw-card-hd {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 18px 22px; border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.sw-card-hd.purple { background: linear-gradient(135deg, #F5F3FF, #EEF2FF); }
|
||||
.sw-card-hd.green { background: linear-gradient(135deg, #ECFDF5, #D1FAE5); }
|
||||
|
||||
.sw-card-hd-icon {
|
||||
width: 38px; height: 38px; border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.sw-card-hd-icon.purple { background: var(--primary); box-shadow: 0 3px 10px rgba(79,70,229,.3); }
|
||||
.sw-card-hd-icon.green { background: var(--success); box-shadow: 0 3px 10px rgba(5,150,105,.3); }
|
||||
.sw-card-hd-icon.orange { background: var(--warning); box-shadow: 0 3px 10px rgba(217,119,6,.3); }
|
||||
|
||||
.sw-card-hd h5 { font-size: 15px; font-weight: 700; margin: 0; }
|
||||
.sw-card-hd h5.purple { color: #3730A3; }
|
||||
.sw-card-hd h5.green { color: #065F46; }
|
||||
.sw-card-hd p { font-size: 12px; margin: 2px 0 0; }
|
||||
.sw-card-hd p.purple { color: #6D6AA4; }
|
||||
.sw-card-hd p.green { color: #047857; }
|
||||
|
||||
.sw-card-bd { padding: 24px 22px; }
|
||||
|
||||
/* ── SECTION DIVIDER ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 11.5px; font-weight: 700;
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-label.purple { color: var(--primary); }
|
||||
.sw-section-label.green { color: var(--success); }
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
}
|
||||
.sw-section-line.purple { background: linear-gradient(to right, #C7D2FE, transparent); }
|
||||
.sw-section-line.green { background: linear-gradient(to right, #A7F3D0, transparent); }
|
||||
|
||||
/* ── FORM GRID ── */
|
||||
.sw-form-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 20px; }
|
||||
.sw-form-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
|
||||
.sw-form-group { display: flex; flex-direction: column; gap: 6px; }
|
||||
.sw-form-group.full { grid-column: 1 / -1; }
|
||||
|
||||
.sw-label { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
|
||||
.sw-label span { color: var(--danger); margin-left: 2px; }
|
||||
|
||||
.sw-input, .sw-select, .sw-textarea {
|
||||
padding: 10px 14px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 14px; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; width: 100%;
|
||||
transition: border-color .15s, box-shadow .15s, background .15s;
|
||||
}
|
||||
.sw-input:focus, .sw-select:focus, .sw-textarea:focus {
|
||||
border-color: var(--primary); background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
|
||||
}
|
||||
.sw-textarea { resize: vertical; min-height: 80px; }
|
||||
|
||||
/* ── SOAL CARD ── */
|
||||
.soal-item {
|
||||
background: var(--surface);
|
||||
border: 1.5px solid #DDD6FE;
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
transition: box-shadow .2s;
|
||||
}
|
||||
.soal-item:hover { box-shadow: 0 4px 16px rgba(79,70,229,.08); }
|
||||
|
||||
.soal-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 18px;
|
||||
background: linear-gradient(135deg, #F5F3FF, #EEF2FF);
|
||||
border-bottom: 1px solid #DDD6FE;
|
||||
}
|
||||
.soal-num {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 13px; font-weight: 700; color: var(--primary);
|
||||
}
|
||||
.soal-num-badge {
|
||||
width: 26px; height: 26px; border-radius: 50%;
|
||||
background: var(--primary); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; font-weight: 800;
|
||||
}
|
||||
|
||||
.soal-body { padding: 18px; }
|
||||
|
||||
.sw-choices-grid {
|
||||
display: grid; grid-template-columns: 1fr 1fr;
|
||||
gap: 10px; margin: 14px 0;
|
||||
}
|
||||
.sw-choice-wrap { position: relative; }
|
||||
.sw-choice-label {
|
||||
position: absolute; left: 12px; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 22px; height: 22px; border-radius: 50%;
|
||||
background: var(--primary-light); color: var(--primary);
|
||||
font-size: 11px; font-weight: 800;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
pointer-events: none; z-index: 1;
|
||||
}
|
||||
.sw-choice-input {
|
||||
width: 100%;
|
||||
padding: 9px 12px 9px 42px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark); background: var(--neutral-light);
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-choice-input:focus {
|
||||
border-color: var(--primary); background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(79,70,229,.1);
|
||||
}
|
||||
|
||||
.sw-answer-row {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.sw-answer-label {
|
||||
font-size: 12px; font-weight: 700; color: var(--success);
|
||||
white-space: nowrap; display: flex; align-items: center; gap: 5px;
|
||||
}
|
||||
.sw-answer-select {
|
||||
flex: 1; padding: 9px 14px;
|
||||
border: 1.5px solid #A7F3D0;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--success-light);
|
||||
font-size: 13.5px; font-weight: 700; color: var(--success);
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
outline: none; transition: all .15s;
|
||||
}
|
||||
.sw-answer-select:focus { border-color: var(--success); box-shadow: 0 0 0 3px rgba(5,150,105,.1); }
|
||||
|
||||
/* ── ADD SOAL BUTTON ── */
|
||||
.sw-add-soal {
|
||||
width: 100%; padding: 14px;
|
||||
border-radius: var(--radius-md);
|
||||
border: 2px dashed #C7D2FE;
|
||||
background: var(--primary-light);
|
||||
color: var(--primary); font-size: 14px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .2s;
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sw-add-soal:hover {
|
||||
background: var(--primary); color: #fff;
|
||||
border-color: var(--primary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* ── FOOTER ── */
|
||||
.sw-footer {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding-top: 22px; border-top: 1px solid var(--border); margin-top: 8px;
|
||||
}
|
||||
.sw-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 11px 22px; border-radius: var(--radius-md);
|
||||
font-size: 14px; font-weight: 700; border: none;
|
||||
cursor: pointer; text-decoration: none;
|
||||
transition: all .18s ease; line-height: 1;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
.sw-btn-secondary { background: var(--surface); color: var(--text-mid); border: 1.5px solid var(--border); }
|
||||
.sw-btn-secondary:hover { background: var(--neutral-light); color: var(--text-dark); }
|
||||
.sw-btn-success { background: var(--success); color: #fff; box-shadow: 0 4px 14px rgba(5,150,105,.3); }
|
||||
.sw-btn-success:hover { background: #047857; transform: translateY(-1px); box-shadow: 0 6px 20px rgba(5,150,105,.35); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sw-page { padding: 14px; }
|
||||
.sw-form-row { grid-template-columns: 1fr; }
|
||||
.sw-form-row-2 { grid-template-columns: 1fr; }
|
||||
.sw-choices-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<a href="{{ route('guru.ujian.index') }}" class="sw-back-btn">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
</a>
|
||||
<div class="sw-topbar-text">
|
||||
<h3>Buat Ujian</h3>
|
||||
<p>Isi data ujian dan tambahkan soal pilihan ganda</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('guru.ujian.store') }}">
|
||||
@csrf
|
||||
|
||||
{{-- ALERTS --}}
|
||||
@if(session('success'))
|
||||
<div class="sw-alert sw-alert-success">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if($errors->any())
|
||||
<div class="sw-alert sw-alert-danger">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
{{ $errors->first() }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- ── CARD 1: PENGATURAN ── --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-banner purple"></div>
|
||||
<div class="sw-card-hd purple">
|
||||
<div class="sw-card-hd-icon orange">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93l-1.41 1.41M4.93 4.93l1.41 1.41M19.07 19.07l-1.41-1.41M4.93 19.07l1.41-1.41M12 2v2M12 20v2M2 12h2M20 12h2"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="purple">Pengaturan Ujian</h5>
|
||||
<p class="purple">Pilih kelas, mapel, dan jenis ujian</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-card-bd">
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label purple">Klasifikasi</span>
|
||||
<div class="sw-section-line purple"></div>
|
||||
</div>
|
||||
<div class="sw-form-row">
|
||||
<!-- <div class="sw-form-group">
|
||||
<label class="sw-label">Kelas <span>*</span></label>
|
||||
<select name="kelas_id" class="sw-select" required>
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelas as $k)
|
||||
<option value="{{ $k->id }}">{{ $k->nama_kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div> -->
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Kelas <span>*</span></label>
|
||||
<div style="background:var(--neutral-light);border:1.5px solid var(--border);border-radius:var(--radius-md);padding:12px 14px;display:flex;flex-wrap:wrap;gap:10px;">
|
||||
@foreach($kelas as $k)
|
||||
<label style="display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;font-weight:600;color:var(--text-mid);background:#fff;border:1.5px solid var(--border);border-radius:8px;padding:6px 12px;transition:all .15s;"
|
||||
onmouseover="this.style.borderColor='#6366f1'" onmouseout="this.style.borderColor=''">
|
||||
<input type="checkbox" name="kelas_ids[]" value="{{ $k->id }}"
|
||||
style="accent-color:var(--primary);width:15px;height:15px;">
|
||||
{{ $k->nama_kelas }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
<div style="font-size:11.5px;color:var(--text-soft);margin-top:4px;">
|
||||
✓ Centang semua kelas yang akan menerima ujian ini
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Jenis Ujian <span>*</span></label>
|
||||
<select name="jenis" class="sw-select" required>
|
||||
<option value="UTS">UTS</option>
|
||||
<option value="UAS">UAS</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Durasi (menit)</label>
|
||||
<input type="number" name="durasi" class="form-control">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Mapel</label>
|
||||
<input type="hidden" name="mapel_id" value="{{ auth()->user()->guru->mapel_id }}">
|
||||
<input type="text" class="sw-input"
|
||||
value="{{ auth()->user()->guru->mapel->nama_mapel ?? '-' }}"
|
||||
disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Mulai</label>
|
||||
<input type="datetime-local" name="mulai" class="form-control">
|
||||
{{-- ── CARD 2: DATA UJIAN ── --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-banner purple"></div>
|
||||
<div class="sw-card-hd purple">
|
||||
<div class="sw-card-hd-icon purple">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="purple">Data Ujian</h5>
|
||||
<p class="purple">Judul, durasi, dan jadwal ujian</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-card-bd">
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label purple">Informasi Umum</span>
|
||||
<div class="sw-section-line purple"></div>
|
||||
</div>
|
||||
<div class="sw-form-row-2" style="margin-bottom:16px;">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Judul <span>*</span></label>
|
||||
<input type="text" name="judul" class="sw-input" placeholder="Judul ujian" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label>Selesai</label>
|
||||
<input type="datetime-local" name="selesai" class="form-control">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Durasi (menit)</label>
|
||||
<input type="number" name="durasi" class="sw-input" placeholder="Contoh: 90">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="sw-section" style="margin-top:4px;">
|
||||
<span class="sw-section-label purple">Jadwal</span>
|
||||
<div class="sw-section-line purple"></div>
|
||||
</div>
|
||||
<div class="sw-form-row-2">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Waktu Mulai</label>
|
||||
<input type="datetime-local" name="mulai" class="sw-input">
|
||||
</div>
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Waktu Selesai</label>
|
||||
<input type="datetime-local" name="selesai" class="sw-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ================= SOAL ================= --}}
|
||||
<h5 class="text-success mb-3">🧠 Soal Ujian</h5>
|
||||
{{-- ── CARD 3: SOAL ── --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-banner green"></div>
|
||||
<div class="sw-card-hd green">
|
||||
<div class="sw-card-hd-icon green">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="green">Soal Ujian</h5>
|
||||
<p class="green">Tambahkan soal pilihan ganda</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-card-bd">
|
||||
|
||||
<div id="soal-container">
|
||||
|
||||
{{-- SOAL PERTAMA --}}
|
||||
<div class="soal-item border p-3 mb-3 rounded">
|
||||
|
||||
<label>Pertanyaan</label>
|
||||
<textarea name="soal[0][pertanyaan]" class="form-control" required></textarea>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-6">
|
||||
<input name="soal[0][a]" class="form-control" placeholder="Pilihan A">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input name="soal[0][b]" class="form-control" placeholder="Pilihan B">
|
||||
</div>
|
||||
<div class="col-md-6 mt-2">
|
||||
<input name="soal[0][c]" class="form-control" placeholder="Pilihan C">
|
||||
</div>
|
||||
<div class="col-md-6 mt-2">
|
||||
<input name="soal[0][d]" class="form-control" placeholder="Pilihan D">
|
||||
<div class="soal-item">
|
||||
<div class="soal-header">
|
||||
<div class="soal-num">
|
||||
<div class="soal-num-badge">1</div>
|
||||
Soal Nomor 1
|
||||
</div>
|
||||
</div>
|
||||
<div class="soal-body">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Pertanyaan <span>*</span></label>
|
||||
<textarea name="soal[0][pertanyaan]" class="sw-textarea" placeholder="Tulis pertanyaan di sini..." required></textarea>
|
||||
</div>
|
||||
|
||||
<select name="soal[0][jawaban]" class="form-control mt-2">
|
||||
<option value="a">Jawaban A</option>
|
||||
<option value="b">Jawaban B</option>
|
||||
<option value="c">Jawaban C</option>
|
||||
<option value="d">Jawaban D</option>
|
||||
</select>
|
||||
<div class="sw-choices-grid">
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">A</div>
|
||||
<input name="soal[0][a]" class="sw-choice-input" placeholder="Pilihan A">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">B</div>
|
||||
<input name="soal[0][b]" class="sw-choice-input" placeholder="Pilihan B">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">C</div>
|
||||
<input name="soal[0][c]" class="sw-choice-input" placeholder="Pilihan C">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">D</div>
|
||||
<input name="soal[0][d]" class="sw-choice-input" placeholder="Pilihan D">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-answer-row">
|
||||
<div class="sw-answer-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
Kunci Jawaban
|
||||
</div>
|
||||
<select name="soal[0][jawaban]" class="sw-answer-select">
|
||||
<option value="a">Jawaban A</option>
|
||||
<option value="b">Jawaban B</option>
|
||||
<option value="c">Jawaban C</option>
|
||||
<option value="d">Jawaban D</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-info mb-3" onclick="tambahSoal()">+ Tambah Soal</button>
|
||||
<button type="button" class="sw-add-soal" onclick="tambahSoal()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Tambah Soal
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('guru.ujian.index') }}" class="btn btn-secondary">
|
||||
← Kembali
|
||||
<div class="sw-footer">
|
||||
<a href="{{ route('guru.ujian.index') }}" class="sw-btn sw-btn-secondary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
Kembali
|
||||
</a>
|
||||
|
||||
<button class="btn btn-success">
|
||||
💾 Simpan Ujian + Soal
|
||||
<button type="submit" class="sw-btn sw-btn-success">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
Simpan Ujian + Soal
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let index = 1;
|
||||
|
||||
function tambahSoal() {
|
||||
let html = `
|
||||
<div class="soal-item border p-3 mb-3 rounded">
|
||||
<label>Pertanyaan</label>
|
||||
<textarea name="soal[${index}][pertanyaan]" class="form-control"></textarea>
|
||||
|
||||
<input name="soal[${index}][a]" class="form-control mt-2" placeholder="A">
|
||||
<input name="soal[${index}][b]" class="form-control mt-2" placeholder="B">
|
||||
<input name="soal[${index}][c]" class="form-control mt-2" placeholder="C">
|
||||
<input name="soal[${index}][d]" class="form-control mt-2" placeholder="D">
|
||||
|
||||
<select name="soal[${index}][jawaban]" class="form-control mt-2">
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
<option value="d">D</option>
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
const html = `
|
||||
<div class="soal-item">
|
||||
<div class="soal-header">
|
||||
<div class="soal-num">
|
||||
<div class="soal-num-badge">${index + 1}</div>
|
||||
Soal Nomor ${index + 1}
|
||||
</div>
|
||||
</div>
|
||||
<div class="soal-body">
|
||||
<div class="sw-form-group">
|
||||
<label class="sw-label">Pertanyaan</label>
|
||||
<textarea name="soal[${index}][pertanyaan]" class="sw-textarea" placeholder="Tulis pertanyaan di sini..."></textarea>
|
||||
</div>
|
||||
<div class="sw-choices-grid">
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">A</div>
|
||||
<input name="soal[${index}][a]" class="sw-choice-input" placeholder="Pilihan A">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">B</div>
|
||||
<input name="soal[${index}][b]" class="sw-choice-input" placeholder="Pilihan B">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">C</div>
|
||||
<input name="soal[${index}][c]" class="sw-choice-input" placeholder="Pilihan C">
|
||||
</div>
|
||||
<div class="sw-choice-wrap">
|
||||
<div class="sw-choice-label">D</div>
|
||||
<input name="soal[${index}][d]" class="sw-choice-input" placeholder="Pilihan D">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-answer-row">
|
||||
<div class="sw-answer-label">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
Kunci Jawaban
|
||||
</div>
|
||||
<select name="soal[${index}][jawaban]" class="sw-answer-select">
|
||||
<option value="a">Jawaban A</option>
|
||||
<option value="b">Jawaban B</option>
|
||||
<option value="c">Jawaban C</option>
|
||||
<option value="d">Jawaban D</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
document.getElementById('soal-container').insertAdjacentHTML('beforeend', html);
|
||||
index++;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,568 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 18px;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 0 !important;
|
||||
background: var(--bg) !important;
|
||||
}
|
||||
|
||||
#content-wrapper {
|
||||
background: var(--bg) !important;
|
||||
}
|
||||
|
||||
.sw-page {
|
||||
padding: 28px 32px;
|
||||
background: var(--bg);
|
||||
min-height: calc(100vh - 70px);
|
||||
}
|
||||
|
||||
/* ── BACK NAV ── */
|
||||
.back-nav {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--text-soft);
|
||||
font-size: 13.5px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
margin-bottom: 22px;
|
||||
transition: color .15s;
|
||||
}
|
||||
|
||||
.back-nav:hover {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-nav svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* ── PAGE HEADER ── */
|
||||
.sw-ph {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.sw-ph-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
background: var(--warning-light);
|
||||
border: 1px solid #FDE68A;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--warning);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sw-ph-icon svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sw-ph-title {
|
||||
font-size: 21px;
|
||||
font-weight: 800;
|
||||
color: var(--text-dark);
|
||||
margin: 0 0 3px;
|
||||
letter-spacing: -.3px;
|
||||
}
|
||||
|
||||
.sw-ph-sub {
|
||||
font-size: 13px;
|
||||
color: var(--text-soft);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── FORM CARD ── */
|
||||
.form-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, .06);
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.fc-hd {
|
||||
padding: 15px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EEF2FF 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.fc-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dot-indigo {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.dot-green {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.dot-orange {
|
||||
background: var(--warning);
|
||||
}
|
||||
|
||||
.fc-label {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #5B21B6;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .6px;
|
||||
}
|
||||
|
||||
.fc-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* ── FORM ELEMENTS ── */
|
||||
.row-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.row-full {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.form-label-sw {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.form-label-sw .req {
|
||||
color: var(--danger);
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.form-control-sw {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
font-size: 13.5px;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
color: var(--text-dark);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
outline: none;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.form-control-sw:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, .12);
|
||||
}
|
||||
|
||||
.form-control-sw:hover:not(:focus) {
|
||||
border-color: #CBD5E1;
|
||||
}
|
||||
|
||||
.form-control-sw::placeholder {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
select.form-control-sw {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 12px center;
|
||||
background-size: 16px;
|
||||
padding-right: 36px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-soft);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* ── JENIS RADIO ── */
|
||||
.jenis-wrap {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.jenis-opt {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.jenis-opt input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.jenis-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 11px 0;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--text-soft);
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
background: var(--surface);
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
.jenis-opt input:checked+.jenis-btn.uts {
|
||||
background: var(--info-light);
|
||||
border-color: #BAE6FD;
|
||||
color: var(--info);
|
||||
}
|
||||
|
||||
.jenis-opt input:checked+.jenis-btn.uas {
|
||||
background: var(--warning-light);
|
||||
border-color: #FDE68A;
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
/* ── FOOTER ── */
|
||||
.form-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 18px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: #FAFAFA;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sw-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 10px 22px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13.5px;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: all .15s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sw-btn-cancel {
|
||||
background: var(--surface);
|
||||
color: var(--text-mid);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.sw-btn-cancel:hover {
|
||||
background: #F1F5F9;
|
||||
color: var(--text-dark);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sw-btn-save {
|
||||
background: var(--warning);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 14px rgba(217, 119, 6, .3);
|
||||
}
|
||||
|
||||
.sw-btn-save:hover {
|
||||
background: #B45309;
|
||||
color: #fff;
|
||||
box-shadow: 0 6px 20px rgba(217, 119, 6, .4);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.sw-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.row-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.jenis-wrap {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- BACK LINK --}}
|
||||
<a href="{{ route('guru.ujian.index') }}" class="back-nav">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Kembali ke Daftar Ujian
|
||||
</a>
|
||||
|
||||
{{-- PAGE HEADER --}}
|
||||
<div class="sw-ph">
|
||||
<div class="sw-ph-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="sw-ph-title">Edit Ujian</h2>
|
||||
<p class="sw-ph-sub">Perbarui informasi dan pengaturan ujian</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('guru.ujian.update', $ujian->id) }}" method="POST" id="formEditUjian">
|
||||
@if ($errors->any())
|
||||
<div style="
|
||||
background:#FEF2F2;
|
||||
border:1px solid #FECACA;
|
||||
color:#B91C1C;
|
||||
padding:14px;
|
||||
border-radius:10px;
|
||||
margin-bottom:18px;
|
||||
">
|
||||
<ul style="margin:0;padding-left:18px;">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
{{-- CARD 1: INFORMASI UJIAN --}}
|
||||
<div class="form-card">
|
||||
<div class="fc-hd">
|
||||
<div class="fc-dot dot-indigo"></div>
|
||||
<span class="fc-label">Informasi Ujian</span>
|
||||
</div>
|
||||
<div class="fc-body">
|
||||
|
||||
<div class="row-full">
|
||||
<label class="form-label-sw">
|
||||
Judul Ujian <span class="req">*</span>
|
||||
</label>
|
||||
<input type="text" name="judul" class="form-control-sw" value="{{ old('judul', $ujian->judul) }}"
|
||||
placeholder="Masukkan judul ujian" required>
|
||||
</div>
|
||||
|
||||
<div class="row-grid">
|
||||
<div>
|
||||
<label class="form-label-sw">
|
||||
Mata Pelajaran <span class="req">*</span>
|
||||
</label>
|
||||
<select name="mapel_id" class="form-control-sw" required>
|
||||
@foreach($mapel as $m)
|
||||
<option value="{{ $m->id }}" {{ $ujian->mapel_id == $m->id ? 'selected' : '' }}>
|
||||
{{ $m->nama_mapel }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label-sw">
|
||||
Kelas <span class="req">*</span>
|
||||
</label>
|
||||
<div
|
||||
style="background:#F9FAFB;border:1px solid #E5E7EB;border-radius:10px;padding:12px 14px;display:flex;flex-wrap:wrap;gap:10px;">
|
||||
@foreach($kelas as $k)
|
||||
<label
|
||||
style="display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;font-weight:600;color:#374151;background:#fff;border:1.5px solid #E5E7EB;border-radius:8px;padding:6px 12px;transition:all .15s;"
|
||||
onmouseover="this.style.borderColor='#6366f1'" onmouseout="this.style.borderColor=''">
|
||||
<input type="checkbox" name="kelas_ids[]" value="{{ $k->id }}" {{ in_array($k->id, $selectedKelas) ? 'checked' : '' }}
|
||||
style="accent-color:#4F46E5;width:15px;height:15px;">
|
||||
{{ $k->nama_kelas }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
<p class="form-hint">✓ Centang semua kelas yang akan menerima ujian ini</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-full">
|
||||
<label class="form-label-sw">
|
||||
Durasi <span class="req">*</span>
|
||||
</label>
|
||||
<input type="number" name="durasi" class="form-control-sw"
|
||||
value="{{ old('durasi', $ujian->durasi) }}" placeholder="Contoh: 90" required>
|
||||
<p class="form-hint">Dalam satuan menit</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- CARD 2: JADWAL --}}
|
||||
<div class="form-card">
|
||||
<div class="fc-hd">
|
||||
<div class="fc-dot dot-green"></div>
|
||||
<span class="fc-label">Jadwal Pelaksanaan</span>
|
||||
</div>
|
||||
<div class="fc-body">
|
||||
|
||||
<div class="row-grid">
|
||||
<div>
|
||||
<label class="form-label-sw">
|
||||
Waktu Mulai <span class="req">*</span>
|
||||
</label>
|
||||
<input type="datetime-local" name="mulai" class="form-control-sw"
|
||||
value="{{ date('Y-m-d\TH:i', strtotime($ujian->mulai)) }}" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label-sw">
|
||||
Waktu Selesai <span class="req">*</span>
|
||||
</label>
|
||||
<input type="datetime-local" name="selesai" class="form-control-sw"
|
||||
value="{{ date('Y-m-d\TH:i', strtotime($ujian->selesai)) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- CARD 3: JENIS UJIAN --}}
|
||||
<div class="form-card">
|
||||
<div class="fc-hd">
|
||||
<div class="fc-dot dot-orange"></div>
|
||||
<span class="fc-label">Jenis Ujian</span>
|
||||
</div>
|
||||
<div class="fc-body">
|
||||
|
||||
<label class="form-label-sw">Pilih Jenis <span class="req">*</span></label>
|
||||
<div class="jenis-wrap">
|
||||
<label class="jenis-opt">
|
||||
<input type="radio" name="jenis" value="UTS" {{ $ujian->jenis == 'UTS' ? 'checked' : '' }}>
|
||||
<div class="jenis-btn uts">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2.2">
|
||||
<path d="M9 11l3 3L22 4" />
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
||||
</svg>
|
||||
UTS — Ujian Tengah Semester
|
||||
</div>
|
||||
</label>
|
||||
<label class="jenis-opt">
|
||||
<input type="radio" name="jenis" value="UAS" {{ $ujian->jenis == 'UAS' ? 'checked' : '' }}>
|
||||
<div class="jenis-btn uas">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2.2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
</svg>
|
||||
UAS — Ujian Akhir Semester
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- FOOTER --}}
|
||||
<div class="form-footer">
|
||||
<a href="{{ route('guru.ujian.index') }}" class="sw-btn sw-btn-cancel">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
Batalkan
|
||||
</a>
|
||||
<button type="submit" class="sw-btn sw-btn-save" id="btnUpdate">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Update Ujian
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('formEditUjian').addEventListener('submit', function () {
|
||||
|
||||
const btn = document.getElementById('btnUpdate');
|
||||
|
||||
btn.disabled = true;
|
||||
|
||||
btn.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
style="animation:spin .8s linear infinite">
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
Menyimpan...
|
||||
`;
|
||||
});
|
||||
|
||||
const s = document.createElement('style');
|
||||
s.textContent = '@keyframes spin{to{transform:rotate(360deg)}}';
|
||||
document.head.appendChild(s);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,12 +1,282 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<a href="{{ route('guru.ujian.create') }}">+ Buat Ujian</a>
|
||||
<ul>
|
||||
@foreach($data as $u)
|
||||
<li>
|
||||
{{ $u->judul }}
|
||||
<a href="{{ route('guru.ujian.soal', $u->id) }}">Input Soal</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
:root {
|
||||
--primary:#4F46E5;--primary-dark:#3730A3;--primary-light:#EEF2FF;
|
||||
--success:#059669;--success-light:#ECFDF5;
|
||||
--warning:#D97706;--warning-light:#FFFBEB;
|
||||
--danger:#DC2626;--danger-light:#FEF2F2;
|
||||
--info:#0284C7;--info-light:#F0F9FF;
|
||||
--neutral-light:#F9FAFB;--bg:#F3F4F8;--surface:#FFFFFF;
|
||||
--border:#E5E7EB;--text-dark:#111827;--text-mid:#374151;--text-soft:#6B7280;
|
||||
--radius-md:10px;--radius-lg:16px;
|
||||
}
|
||||
*{font-family:'Plus Jakarta Sans',sans-serif;box-sizing:border-box;}
|
||||
#content{padding:0!important;background:var(--bg)!important;}
|
||||
#content-wrapper{background:var(--bg)!important;}
|
||||
.sw-page{padding:28px 32px;background:var(--bg);min-height:calc(100vh - 70px);}
|
||||
.sw-topbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:26px;gap:16px;flex-wrap:wrap;}
|
||||
.sw-topbar h3{font-size:21px;font-weight:800;color:var(--text-dark);margin:0 0 3px;letter-spacing:-.3px;}
|
||||
.sw-topbar p{font-size:13px;color:var(--text-soft);margin:0;}
|
||||
.sw-btn{display:inline-flex;align-items:center;gap:7px;padding:10px 20px;border-radius:var(--radius-md);font-size:13.5px;font-weight:700;border:none;cursor:pointer;text-decoration:none;transition:all .18s ease;line-height:1;font-family:'Plus Jakarta Sans',sans-serif;}
|
||||
.sw-btn-primary{background:var(--primary);color:#fff;box-shadow:0 4px 14px rgba(79,70,229,.3);}
|
||||
.sw-btn-primary:hover{background:var(--primary-dark);color:#fff;transform:translateY(-1px);}
|
||||
.sw-btn-sm{padding:7px 13px;font-size:12.5px;border-radius:8px;}
|
||||
.sw-btn-warning{background:var(--warning-light);color:var(--warning);border:1px solid #FDE68A;}
|
||||
.sw-btn-warning:hover{background:var(--warning);color:#fff;}
|
||||
.sw-btn-danger{background:var(--danger-light);color:var(--danger);border:1px solid #FECACA;}
|
||||
.sw-btn-danger:hover{background:var(--danger);color:#fff;}
|
||||
.sw-btn-success{background:var(--success);color:#fff;box-shadow:0 4px 14px rgba(5,150,105,.3);}
|
||||
.sw-btn-success:hover{background:#047857;color:#fff;transform:translateY(-1px);}
|
||||
.sw-btn-info{background:var(--info-light);color:var(--info);border:1px solid #BAE6FD;}
|
||||
.sw-btn-info:hover{background:var(--info);color:#fff;}
|
||||
.sw-card{background:var(--surface);border-radius:var(--radius-lg);border:1px solid var(--border);box-shadow:0 1px 3px rgba(0,0,0,.05);overflow:hidden;margin-bottom:22px;}
|
||||
.sw-card-hd{display:flex;align-items:center;justify-content:space-between;padding:16px 22px;border-bottom:1px solid var(--border);background:linear-gradient(135deg,#F5F3FF 0%,#EEF2FF 100%);}
|
||||
.sw-card-hd-left{display:flex;align-items:center;gap:10px;}
|
||||
.sw-card-hd-icon{width:34px;height:34px;border-radius:9px;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;}
|
||||
.sw-card-hd h6{font-size:14px;font-weight:700;color:#3730A3;margin:0;}
|
||||
.sw-card-hd p{font-size:11.5px;color:#6D6AA4;margin:2px 0 0;}
|
||||
.sw-count-badge{background:var(--primary-light);border:1px solid #C7D2FE;border-radius:99px;padding:3px 12px;font-size:12px;font-weight:700;color:var(--primary);}
|
||||
.sw-ujian-list{padding:8px 0;}
|
||||
.sw-ujian-item{display:flex;align-items:center;gap:18px;padding:18px 22px;border-bottom:1px solid var(--border);transition:background .15s;flex-wrap:wrap;}
|
||||
.sw-ujian-item:last-child{border-bottom:none;}
|
||||
.sw-ujian-item:hover{background:#FAFAFF;}
|
||||
.sw-ujian-icon{width:46px;height:46px;border-radius:12px;background:var(--primary-light);border:1px solid #C7D2FE;display:flex;align-items:center;justify-content:center;color:var(--primary);flex-shrink:0;}
|
||||
.sw-ujian-info{flex:1;min-width:0;}
|
||||
.sw-ujian-title{font-size:15px;font-weight:700;color:var(--text-dark);margin-bottom:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||||
.sw-ujian-meta{display:flex;gap:10px;flex-wrap:wrap;}
|
||||
.sw-meta-chip{display:inline-flex;align-items:center;gap:5px;background:var(--neutral-light);border:1px solid var(--border);border-radius:7px;padding:3px 10px;font-size:11.5px;font-weight:600;color:var(--text-soft);}
|
||||
.sw-jenis{display:inline-flex;align-items:center;padding:3px 10px;border-radius:99px;font-size:11px;font-weight:800;letter-spacing:.5px;}
|
||||
.sw-jenis-uts{background:var(--info-light);border:1px solid #BAE6FD;color:var(--info);}
|
||||
.sw-jenis-uas{background:var(--warning-light);border:1px solid #FDE68A;color:var(--warning);}
|
||||
.sw-status-draft{display:inline-flex;align-items:center;gap:5px;background:#F3F4F6;border:1px solid #D1D5DB;border-radius:99px;padding:3px 10px;font-size:11px;font-weight:700;color:#6B7280;}
|
||||
.sw-status-kirim{display:inline-flex;align-items:center;gap:5px;background:var(--success-light);border:1px solid #A7F3D0;border-radius:99px;padding:3px 10px;font-size:11px;font-weight:700;color:var(--success);}
|
||||
.sw-ujian-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;flex-wrap:wrap;}
|
||||
.sw-empty{padding:70px 20px;text-align:center;}
|
||||
.sw-empty-ring{width:80px;height:80px;border-radius:50%;background:var(--primary-light);border:2px solid #C7D2FE;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;color:var(--primary);}
|
||||
.sw-empty h5{font-size:16px;font-weight:700;color:var(--text-dark);margin-bottom:6px;}
|
||||
.sw-empty p{font-size:13px;color:var(--text-soft);margin-bottom:20px;}
|
||||
|
||||
/* NILAI SECTION */
|
||||
.sw-nilai-card{background:var(--surface);border-radius:var(--radius-lg);border:1px solid var(--border);box-shadow:0 1px 3px rgba(0,0,0,.05);overflow:hidden;}
|
||||
.sw-nilai-hd{display:flex;align-items:center;justify-content:space-between;padding:16px 22px;border-bottom:1px solid var(--border);background:linear-gradient(135deg,#ECFDF5,#D1FAE5);}
|
||||
.sw-nilai-filter{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
||||
.sw-select-kelas{padding:8px 14px;border:1.5px solid var(--border);border-radius:var(--radius-md);font-size:13px;font-weight:600;color:var(--text-dark);background:var(--neutral-light);font-family:'Plus Jakarta Sans',sans-serif;outline:none;}
|
||||
.sw-select-kelas:focus{border-color:var(--success);box-shadow:0 0 0 3px rgba(5,150,105,.1);}
|
||||
.sw-table{width:100%;border-collapse:collapse;}
|
||||
.sw-table th{padding:11px 16px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--text-soft);background:var(--neutral-light);border-bottom:1px solid var(--border);text-align:left;}
|
||||
.sw-table td{padding:12px 16px;font-size:13.5px;color:var(--text-mid);border-bottom:1px solid var(--border);}
|
||||
.sw-table tr:last-child td{border-bottom:none;}
|
||||
.sw-table tr:hover td{background:#FAFAFF;}
|
||||
.sw-nilai-badge{display:inline-flex;align-items:center;justify-content:center;min-width:44px;padding:4px 10px;border-radius:99px;font-size:13px;font-weight:800;}
|
||||
.n-a{background:#DCFCE7;color:#166534;}
|
||||
.n-b{background:#FEF9C3;color:#854D0E;}
|
||||
.n-c{background:#FEE2E2;color:#991B1B;}
|
||||
|
||||
@media(max-width:768px){
|
||||
.sw-page{padding:14px;}
|
||||
.sw-ujian-actions{width:100%;justify-content:flex-end;}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<div>
|
||||
<h3>Manajemen Ujian</h3>
|
||||
<p>Kelola ujian dan pantau nilai siswa</p>
|
||||
</div>
|
||||
<a href="{{ route('guru.ujian.create') }}" class="sw-btn sw-btn-primary">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||||
Buat Ujian
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- ALERT --}}
|
||||
@if(session('success'))
|
||||
<div style="display:flex;align-items:center;gap:10px;background:var(--success-light);border:1px solid #A7F3D0;border-radius:var(--radius-md);padding:13px 16px;margin-bottom:20px;font-size:13px;font-weight:600;color:var(--success);">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- DAFTAR UJIAN --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-left">
|
||||
<div class="sw-card-hd-icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h6>Daftar Ujian</h6>
|
||||
<p>Semua ujian yang telah dibuat</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="sw-count-badge">{{ $data->count() }} Ujian</span>
|
||||
</div>
|
||||
|
||||
<div class="sw-ujian-list">
|
||||
@forelse($data as $u)
|
||||
<div class="sw-ujian-item">
|
||||
<div class="sw-ujian-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
</div>
|
||||
|
||||
<div class="sw-ujian-info">
|
||||
<div class="sw-ujian-title">{{ $u->judul }}</div>
|
||||
<div class="sw-ujian-meta">
|
||||
@if($u->jenis)
|
||||
<span class="sw-jenis {{ $u->jenis == 'UTS' ? 'sw-jenis-uts' : 'sw-jenis-uas' }}">
|
||||
{{ $u->jenis }}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
{{-- STATUS KIRIM --}}
|
||||
@if($u->status_kirim == 'terkirim')
|
||||
<span class="sw-status-kirim">
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M22 2L11 13"/><path d="M22 2L15 22 11 13 2 9l20-7z"/></svg>
|
||||
Terkirim ke Siswa
|
||||
</span>
|
||||
@else
|
||||
<span class="sw-status-draft">
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
Draft
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<span class="sw-meta-chip">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
{{ $u->durasi ?? '-' }} menit
|
||||
</span>
|
||||
|
||||
{{-- KELAS --}}
|
||||
@foreach($u->kelas as $k)
|
||||
<span class="sw-meta-chip" style="background:var(--primary-light);border-color:#C7D2FE;color:var(--primary);">
|
||||
{{ $k->nama_kelas }}
|
||||
</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-ujian-actions">
|
||||
{{-- Tombol Kirim (hanya kalau masih draft) --}}
|
||||
@if($u->status_kirim == 'draft')
|
||||
<form action="{{ route('guru.ujian.kirim', $u->id) }}" method="POST" class="d-inline"
|
||||
onsubmit="return confirm('Kirim ujian ini ke semua kelas yang dipilih?')">
|
||||
@csrf
|
||||
<button type="submit" class="sw-btn sw-btn-sm sw-btn-success">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M22 2L11 13"/><path d="M22 2L15 22 11 13 2 9l20-7z"/></svg>
|
||||
Kirim ke Siswa
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<a href="{{ route('guru.ujian.edit', $u->id) }}" class="sw-btn sw-btn-sm sw-btn-warning">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<form action="{{ route('guru.ujian.destroy', $u->id) }}" method="POST" class="d-inline"
|
||||
onsubmit="return confirm('Yakin hapus ujian ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="sw-btn sw-btn-sm sw-btn-danger">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
|
||||
Hapus
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="sw-empty">
|
||||
<div class="sw-empty-ring">
|
||||
<svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
</div>
|
||||
<h5>Belum Ada Ujian</h5>
|
||||
<p>Klik tombol di atas untuk membuat ujian pertama</p>
|
||||
<a href="{{ route('guru.ujian.create') }}" class="sw-btn sw-btn-primary">+ Buat Ujian</a>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ══ NILAI SISWA ══ --}}
|
||||
<div class="sw-nilai-card">
|
||||
<div class="sw-nilai-hd">
|
||||
<div style="display:flex;align-items:center;gap:10px;">
|
||||
<div style="width:34px;height:34px;border-radius:9px;background:var(--success);color:#fff;display:flex;align-items:center;justify-content:center;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:14px;font-weight:700;color:#065F46;">Nilai Siswa</div>
|
||||
<div style="font-size:11.5px;color:#047857;margin-top:2px;">Filter berdasarkan kelas</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FILTER KELAS --}}
|
||||
<form method="GET" action="{{ route('guru.ujian.index') }}" style="display:flex;gap:8px;align-items:center;">
|
||||
<select name="kelas_id" class="sw-select-kelas" onchange="this.form.submit()">
|
||||
<option value="">-- Pilih Kelas --</option>
|
||||
@foreach($kelasList as $k)
|
||||
<option value="{{ $k->id }}" {{ $selectedKelas == $k->id ? 'selected' : '' }}>
|
||||
{{ $k->nama_kelas }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@if($selectedKelas)
|
||||
<a href="{{ route('guru.ujian.index') }}" style="font-size:12px;color:var(--text-soft);text-decoration:none;font-weight:600;">✕ Reset</a>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if($selectedKelas && $nilaiSiswa->count())
|
||||
<table class="sw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nama Siswa</th>
|
||||
<th>Ujian</th>
|
||||
<th>Jenis</th>
|
||||
<th>Nilai</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($nilaiSiswa as $i => $n)
|
||||
<tr>
|
||||
<td style="color:var(--text-soft);font-weight:600;">{{ $i + 1 }}</td>
|
||||
<td style="font-weight:700;color:var(--text-dark);">{{ $n->siswa->nama ?? '-' }}</td>
|
||||
<td>{{ $n->ujian->judul ?? '-' }}</td>
|
||||
<td>
|
||||
@if($n->ujian && $n->ujian->jenis)
|
||||
<span class="sw-jenis {{ $n->ujian->jenis == 'UTS' ? 'sw-jenis-uts' : 'sw-jenis-uas' }}">
|
||||
{{ $n->ujian->jenis }}
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@php $v = $n->nilai; @endphp
|
||||
<span class="sw-nilai-badge {{ $v >= 75 ? 'n-a' : ($v >= 60 ? 'n-b' : 'n-c') }}">
|
||||
{{ $v }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@elseif($selectedKelas)
|
||||
<div style="padding:40px;text-align:center;color:var(--text-soft);font-size:13px;font-weight:600;">
|
||||
Belum ada siswa yang mengerjakan ujian di kelas ini.
|
||||
</div>
|
||||
@else
|
||||
<div style="padding:40px;text-align:center;color:var(--text-soft);font-size:13px;font-weight:600;">
|
||||
Pilih kelas di atas untuk melihat nilai siswa.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,34 +1,120 @@
|
|||
@extends('layouts.app')
|
||||
@section('content')
|
||||
|
||||
<h3>Input Soal: {{ $ujian->judul }}</h3>
|
||||
<div class="container my-5" style="max-width: 700px;">
|
||||
|
||||
@if(session('success')) <div>{{ session('success') }}</div> @endif
|
||||
@if($errors->any()) <div>{{ $errors->first() }}</div> @endif
|
||||
<h3 class="mb-4 fw-500" style="border-left: 4px solid #1d4ed8; padding-left: 12px; color: #1a2e4a;">
|
||||
Input Soal: {{ $ujian->judul }}
|
||||
</h3>
|
||||
|
||||
<form method="POST" action="{{ route('guru.ujian.storeSoal', $ujian->id) }}">
|
||||
@csrf
|
||||
@if(session('success'))
|
||||
<div class="alert alert-dismissible fade show d-flex align-items-center gap-2" role="alert"
|
||||
style="background:#dcfce7; color:#166534; border: 0.5px solid #86efac; border-radius: 8px;">
|
||||
<i class="bi bi-check-circle"></i>
|
||||
{{ session('success') }}
|
||||
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@for($i=0; $i<5; $i++)
|
||||
<p>Soal {{ $i+1 }}</p>
|
||||
@if($errors->any())
|
||||
<div class="alert alert-dismissible fade show d-flex align-items-center gap-2" role="alert"
|
||||
style="background:#fef2f2; color:#991b1b; border: 0.5px solid #fca5a5; border-radius: 8px;">
|
||||
<i class="bi bi-exclamation-circle"></i>
|
||||
{{ $errors->first() }}
|
||||
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<input type="text" name="soal[{{ $i }}][pertanyaan]" placeholder="Pertanyaan" required><br>
|
||||
<form method="POST" action="{{ route('guru.ujian.storeSoal', $ujian->id) }}">
|
||||
@csrf
|
||||
|
||||
<input type="text" name="soal[{{ $i }}][a]" placeholder="A" required><br>
|
||||
<input type="text" name="soal[{{ $i }}][b]" placeholder="B" required><br>
|
||||
<input type="text" name="soal[{{ $i }}][c]" placeholder="C" required><br>
|
||||
<input type="text" name="soal[{{ $i }}][d]" placeholder="D" required><br>
|
||||
@for($i=0; $i<5; $i++)
|
||||
<div class="card mb-4" style="border: 0.5px solid #e2e8f0; border-radius: 12px; overflow: hidden;">
|
||||
|
||||
<select name="soal[{{ $i }}][jawaban]" required>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
<option value="d">D</option>
|
||||
</select>
|
||||
<hr>
|
||||
@endfor
|
||||
<div class="card-header d-flex align-items-center gap-2"
|
||||
style="background: #1a2e4a; color: #e0eeff; padding: 12px 20px; border: none;">
|
||||
<i class="bi bi-file-text" style="font-size: 16px;"></i>
|
||||
Soal
|
||||
<span style="background: rgba(255,255,255,0.15); border-radius: 20px; padding: 2px 10px;
|
||||
font-size: 12px; color: #bfdbfe;">
|
||||
{{ $i + 1 }} / 5
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button>Simpan Soal</button>
|
||||
</form>
|
||||
<div class="card-body p-4">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="pertanyaan-{{ $i }}" class="form-label"
|
||||
style="font-size: 13px; font-weight: 500; color: #1a2e4a;">
|
||||
<i class="bi bi-question-circle" style="margin-right: 4px; color: #1d4ed8;"></i>
|
||||
Pertanyaan
|
||||
</label>
|
||||
<input type="text" id="pertanyaan-{{ $i }}" name="soal[{{ $i }}][pertanyaan]"
|
||||
class="form-control" placeholder="Masukkan pertanyaan soal ke-{{ $i+1 }}..."
|
||||
style="font-size: 14px; border: 0.5px solid #cbd5e1; border-radius: 8px;"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" style="font-size: 13px; font-weight: 500; color: #1a2e4a;">
|
||||
Pilihan Jawaban
|
||||
</label>
|
||||
<div class="row g-2">
|
||||
@foreach(['a','b','c','d'] as $opt)
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="d-flex align-items-center gap-1 mb-1">
|
||||
<span style="width: 20px; height: 20px; border-radius: 50%;
|
||||
background: #1a2e4a; color: #e0eeff; display: inline-flex;
|
||||
align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 500;">
|
||||
{{ strtoupper($opt) }}
|
||||
</span>
|
||||
</div>
|
||||
<input type="text" id="{{ $opt }}-{{ $i }}"
|
||||
name="soal[{{ $i }}][{{ $opt }}]"
|
||||
class="form-control"
|
||||
placeholder="Pilihan {{ strtoupper($opt) }}"
|
||||
style="font-size: 14px; border: 0.5px solid #cbd5e1; border-radius: 8px;"
|
||||
required>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="border: none; border-top: 0.5px solid #e2e8f0; margin: 1rem 0;">
|
||||
|
||||
<div class="mb-0">
|
||||
<label for="jawaban-{{ $i }}" class="form-label"
|
||||
style="font-size: 13px; font-weight: 500; color: #1a2e4a;">
|
||||
<i class="bi bi-check-circle" style="margin-right: 4px; color: #1d4ed8;"></i>
|
||||
Jawaban Benar
|
||||
</label>
|
||||
<select id="jawaban-{{ $i }}" name="soal[{{ $i }}][jawaban]"
|
||||
class="form-select"
|
||||
style="font-size: 14px; border: 0.5px solid #cbd5e1; border-radius: 8px;"
|
||||
required>
|
||||
<option value="" disabled selected>Pilih jawaban yang benar</option>
|
||||
<option value="a">A</option>
|
||||
<option value="b">B</option>
|
||||
<option value="c">C</option>
|
||||
<option value="d">D</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endfor
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-lg d-flex align-items-center justify-content-center gap-2"
|
||||
style="background: #1a2e4a; color: #e0eeff; border: none; border-radius: 8px;
|
||||
padding: 13px; font-size: 15px; font-weight: 500;">
|
||||
<i class="bi bi-floppy"></i>
|
||||
Simpan Soal
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,117 +1,105 @@
|
|||
{{-- ================================================================
|
||||
FILE: resources/views/layouts/app.blade.php
|
||||
================================================================ --}}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>SMAN 1</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
{{-- Font Awesome --}}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
|
||||
|
||||
<title>SB Admin 2 - Blank</title>
|
||||
|
||||
<!-- Custom fonts for this template-->
|
||||
<link href= "{{ asset('/') }}sbadmin2/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
|
||||
{{-- Google Fonts --}}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
|
||||
href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this template-->
|
||||
<link href="{{ asset('/') }}sbadmin2/css/sb-admin-2.min.css" rel="stylesheet">
|
||||
{{-- Chart.js --}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
background: #f4f6fa;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
/* Geser konten agar tidak tertimpa sidebar (240px) dan topbar (60px) */
|
||||
#main-content {
|
||||
margin-left: 240px;
|
||||
padding: 72px 24px 24px;
|
||||
min-height: 100vh;
|
||||
background: #f4f6fa;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#main-content {
|
||||
margin-left: 0;
|
||||
padding: 72px 16px 20px;
|
||||
}
|
||||
|
||||
.custom-sidebar {
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
.custom-sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@stack('styles')
|
||||
</head>
|
||||
<style>
|
||||
.btn {
|
||||
position: relative;
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.dropdown-menu {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.topbar .dropdown-menu {
|
||||
z-index: 9999 !important;
|
||||
position: absolute !important;
|
||||
}
|
||||
</style>
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Page Wrapper -->
|
||||
<div id="wrapper">
|
||||
<body>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<x-sidebar/>
|
||||
<!-- End of Sidebar -->
|
||||
{{-- SIDEBAR → resources/views/components/sidebar.blade.php --}}
|
||||
@include('components.sidebar')
|
||||
|
||||
<!-- Content Wrapper -->
|
||||
<div id="content-wrapper" class="d-flex flex-column">
|
||||
{{-- TOPBAR → resources/views/components/topbar.blade.php --}}
|
||||
@include('components.topbar')
|
||||
|
||||
<!-- Main Content -->
|
||||
<div id="content">
|
||||
|
||||
<!-- Topbar -->
|
||||
<x-topbar/>
|
||||
<!-- End of Topbar -->
|
||||
|
||||
<!-- Begin Page Content -->
|
||||
@yield('content')
|
||||
<!-- /.container-fluid -->
|
||||
|
||||
</div>
|
||||
<!-- End of Main Content -->
|
||||
|
||||
<!-- Footer -->
|
||||
<x-footer/>
|
||||
<!-- End of Footer -->
|
||||
|
||||
</div>
|
||||
<!-- End of Content Wrapper -->
|
||||
|
||||
</div>
|
||||
<!-- End of Page Wrapper -->
|
||||
|
||||
<!-- Scroll to Top Button-->
|
||||
<a class="scroll-to-top rounded" href="#page-top">
|
||||
<i class="fas fa-angle-up"></i>
|
||||
</a>
|
||||
|
||||
<!-- Logout Modal-->
|
||||
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
|
||||
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
|
||||
<a class="btn btn-primary" href="login.html">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{-- KONTEN UTAMA --}}
|
||||
<div id="main-content">
|
||||
@yield('content')
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap core JavaScript-->
|
||||
<script src="{{ asset('/') }}sbadmin2/vendor/jquery/jquery.min.js"></script>
|
||||
<script src="{{ asset('/') }}sbadmin2/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const sidebarToggleBtn = document.getElementById('sidebarToggleTop');
|
||||
const sidebar = document.querySelector('.custom-sidebar');
|
||||
|
||||
<!-- Core plugin JavaScript-->
|
||||
<script src="{{ asset('/') }}sbadmin2/vendor/jquery-easing/jquery.easing.min.js"></script>
|
||||
if (sidebarToggleBtn && sidebar) {
|
||||
sidebarToggleBtn.addEventListener('click', function () {
|
||||
sidebar.classList.toggle('open');
|
||||
});
|
||||
|
||||
<!-- Custom scripts for all pages-->
|
||||
<script src="{{ asset('/') }}sbadmin2/js/sb-admin-2.min.js"></script>
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display:none;">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
if (window.innerWidth <= 768) {
|
||||
if (!sidebar.contains(e.target) && !sidebarToggleBtn.contains(e.target)) {
|
||||
sidebar.classList.remove('open');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,25 +1,683 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
|
||||
<h1 class="h3 mb-4 text-gray-800">Dashboard Admin</h1>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body text-center">
|
||||
:root {
|
||||
--ink: #0c0f1a;
|
||||
--ink-2: #1e2235;
|
||||
--ink-3: #2d3452;
|
||||
--slate: #64748b;
|
||||
--muted: #94a3b8;
|
||||
--dim: #cbd5e1;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f6f8fc;
|
||||
--surface-3: #eef1f8;
|
||||
|
||||
<h4>Selamat Datang 👑</h4>
|
||||
--blue: #2563eb;
|
||||
--blue-2: #1d4ed8;
|
||||
--blue-glow: rgba(37,99,235,0.18);
|
||||
--blue-soft: #eff6ff;
|
||||
--blue-border:#bfdbfe;
|
||||
|
||||
@auth
|
||||
{{ auth()->user()->name }}
|
||||
@endauth
|
||||
--green: #059669;
|
||||
--green-soft:#ecfdf5;
|
||||
--green-border:#a7f3d0;
|
||||
|
||||
<p class="text-muted">
|
||||
Anda login sebagai <b>Admin</b>
|
||||
</p>
|
||||
--amber: #d97706;
|
||||
--amber-soft:#fffbeb;
|
||||
--amber-border:#fde68a;
|
||||
|
||||
--red: #dc2626;
|
||||
--red-soft: #fef2f2;
|
||||
--red-border:#fecaca;
|
||||
|
||||
--violet: #7c3aed;
|
||||
--violet-soft:#f5f3ff;
|
||||
--violet-border:#ddd6fe;
|
||||
|
||||
--r-sm:5px; --r-md:10px; --r-lg:16px; --r-xl:22px; --r-2xl:30px;
|
||||
--shadow-card: 0 2px 12px rgba(12,15,26,0.06), 0 1px 3px rgba(12,15,26,0.04);
|
||||
--shadow-hover: 0 8px 28px rgba(12,15,26,0.10), 0 2px 8px rgba(12,15,26,0.06);
|
||||
--shadow-blue: 0 4px 20px rgba(37,99,235,0.22);
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing:border-box; margin:0; padding:0; }
|
||||
|
||||
body { background: var(--surface-2); }
|
||||
|
||||
.db { font-family:'Sora',sans-serif; padding: 28px 32px 48px; }
|
||||
|
||||
/* ─── HERO GREETING ─────────────────────────────────────── */
|
||||
.greeting-band {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
background: var(--ink);
|
||||
border-radius: var(--r-2xl);
|
||||
padding: 32px 36px;
|
||||
margin-bottom: 28px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.greeting-band::before {
|
||||
content:'';
|
||||
position:absolute; inset:0;
|
||||
background:
|
||||
radial-gradient(ellipse 60% 80% at 90% 50%, rgba(37,99,235,0.22) 0%, transparent 70%),
|
||||
radial-gradient(ellipse 40% 60% at 10% 0%, rgba(124,58,237,0.14) 0%, transparent 60%);
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
.greeting-band::after {
|
||||
content:'';
|
||||
position:absolute;
|
||||
right:-60px; top:-60px;
|
||||
width:260px; height:260px;
|
||||
border-radius:50%;
|
||||
background: rgba(37,99,235,0.06);
|
||||
border: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
.greeting-text { position:relative; z-index:1; }
|
||||
|
||||
.greeting-tag {
|
||||
display:inline-flex; align-items:center; gap:7px;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius:999px;
|
||||
padding:5px 14px;
|
||||
font-size:12px; font-weight:600; color:rgba(255,255,255,0.7);
|
||||
letter-spacing:0.5px;
|
||||
text-transform:uppercase;
|
||||
margin-bottom:14px;
|
||||
}
|
||||
|
||||
.greeting-tag span {
|
||||
width:7px; height:7px; border-radius:50%;
|
||||
background:#22c55e;
|
||||
box-shadow:0 0 8px #22c55e;
|
||||
animation: pulse-dot 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0%,100% { opacity:1; transform:scale(1); }
|
||||
50% { opacity:0.6; transform:scale(0.8); }
|
||||
}
|
||||
|
||||
.greeting-name {
|
||||
font-size:28px; font-weight:800; color:#fff;
|
||||
line-height:1.15; letter-spacing:-0.5px;
|
||||
margin-bottom:8px;
|
||||
}
|
||||
|
||||
.greeting-name em { font-style:normal; color:#93c5fd; }
|
||||
|
||||
.greeting-sub {
|
||||
font-size:13.5px; color:rgba(255,255,255,0.5);
|
||||
font-weight:400; line-height:1.5;
|
||||
}
|
||||
|
||||
.greeting-date-box {
|
||||
position:relative; z-index:1;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius:var(--r-lg);
|
||||
padding:18px 22px;
|
||||
text-align:center;
|
||||
backdrop-filter:blur(8px);
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.date-day {
|
||||
font-size:38px; font-weight:800; color:#fff;
|
||||
line-height:1; font-family:'Sora',sans-serif;
|
||||
letter-spacing:-1px;
|
||||
}
|
||||
|
||||
.date-month {
|
||||
font-size:13px; font-weight:500; color:rgba(255,255,255,0.5);
|
||||
text-transform:uppercase; letter-spacing:1px;
|
||||
margin-top:4px;
|
||||
}
|
||||
|
||||
.date-time {
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:12px; font-weight:500;
|
||||
color:#93c5fd;
|
||||
margin-top:8px;
|
||||
background: rgba(37,99,235,0.2);
|
||||
border-radius:var(--r-sm);
|
||||
padding:3px 8px;
|
||||
}
|
||||
|
||||
/* ─── STATS GRID ─────────────────────────────────────────── */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--surface-3);
|
||||
border-radius: var(--r-xl);
|
||||
padding: 22px 20px;
|
||||
box-shadow: var(--shadow-card);
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
box-shadow: var(--shadow-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content:'';
|
||||
position:absolute; top:0; left:0; right:0;
|
||||
height:3px;
|
||||
border-radius:var(--r-xl) var(--r-xl) 0 0;
|
||||
}
|
||||
|
||||
.stat-card.blue::before { background: linear-gradient(90deg, var(--blue), #60a5fa); }
|
||||
.stat-card.green::before { background: linear-gradient(90deg, var(--green), #34d399); }
|
||||
.stat-card.amber::before { background: linear-gradient(90deg, var(--amber), #fbbf24); }
|
||||
.stat-card.violet::before { background: linear-gradient(90deg, var(--violet), #a78bfa); }
|
||||
|
||||
.stat-icon-wrap {
|
||||
width:44px; height:44px;
|
||||
border-radius:var(--r-md);
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
margin-bottom:16px;
|
||||
}
|
||||
|
||||
.stat-card.blue .stat-icon-wrap { background:var(--blue-soft); border:1px solid var(--blue-border); }
|
||||
.stat-card.green .stat-icon-wrap { background:var(--green-soft); border:1px solid var(--green-border); }
|
||||
.stat-card.amber .stat-icon-wrap { background:var(--amber-soft); border:1px solid var(--amber-border); }
|
||||
.stat-card.violet.stat-icon-wrap { background:var(--violet-soft); border:1px solid var(--violet-border); }
|
||||
.stat-card.violet .stat-icon-wrap { background:var(--violet-soft); border:1px solid var(--violet-border); }
|
||||
|
||||
.stat-icon-wrap svg { width:20px; height:20px; }
|
||||
.stat-card.blue .stat-icon-wrap svg { color:var(--blue); }
|
||||
.stat-card.green .stat-icon-wrap svg { color:var(--green); }
|
||||
.stat-card.amber .stat-icon-wrap svg { color:var(--amber); }
|
||||
.stat-card.violet .stat-icon-wrap svg { color:var(--violet); }
|
||||
|
||||
.stat-label {
|
||||
font-size:11.5px; font-weight:600; color:var(--muted);
|
||||
text-transform:uppercase; letter-spacing:0.7px;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size:30px; font-weight:800; color:var(--ink);
|
||||
letter-spacing:-1px; line-height:1;
|
||||
font-family:'Sora',sans-serif;
|
||||
}
|
||||
|
||||
.stat-value.mono {
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:26px;
|
||||
}
|
||||
|
||||
.stat-sub {
|
||||
font-size:12px; color:var(--muted);
|
||||
margin-top:6px;
|
||||
}
|
||||
|
||||
/* ─── MAIN 2-COL ─────────────────────────────────────────── */
|
||||
.main-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
/* ─── SECTION CARD ───────────────────────────────────────── */
|
||||
.section-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--surface-3);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sc-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 22px 14px;
|
||||
border-bottom: 1px solid var(--surface-3);
|
||||
}
|
||||
|
||||
.sc-head-left { display:flex; align-items:center; gap:10px; }
|
||||
|
||||
.sc-icon {
|
||||
width:36px; height:36px;
|
||||
border-radius:var(--r-md);
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.sc-icon.blue { background:var(--blue-soft); border:1px solid var(--blue-border); }
|
||||
.sc-icon.green { background:var(--green-soft); border:1px solid var(--green-border); }
|
||||
.sc-icon.amber { background:var(--amber-soft); border:1px solid var(--amber-border); }
|
||||
.sc-icon.violet { background:var(--violet-soft); border:1px solid var(--violet-border); }
|
||||
.sc-icon svg { width:17px; height:17px; }
|
||||
.sc-icon.blue svg { color:var(--blue); }
|
||||
.sc-icon.green svg { color:var(--green); }
|
||||
.sc-icon.amber svg { color:var(--amber); }
|
||||
.sc-icon.violet svg { color:var(--violet); }
|
||||
|
||||
.sc-title {
|
||||
font-size:14px; font-weight:700; color:var(--ink);
|
||||
letter-spacing:-0.2px;
|
||||
}
|
||||
|
||||
.sc-badge {
|
||||
font-size:11.5px; font-weight:600; color:var(--muted);
|
||||
background:var(--surface-3); border:1px solid var(--dim);
|
||||
border-radius:999px; padding:2px 10px;
|
||||
}
|
||||
|
||||
/* ─── NILAI LIST ─────────────────────────────────────────── */
|
||||
.nilai-list { padding: 6px 0; flex:1; }
|
||||
|
||||
.nilai-row {
|
||||
display: flex; align-items: center; gap:12px;
|
||||
padding: 13px 22px;
|
||||
border-bottom: 1px solid var(--surface-3);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.nilai-row:last-child { border-bottom:none; }
|
||||
.nilai-row:hover { background: var(--surface-2); }
|
||||
|
||||
.nilai-mapel-dot {
|
||||
width:9px; height:9px; border-radius:50%; flex-shrink:0;
|
||||
background: var(--blue);
|
||||
}
|
||||
|
||||
.nilai-mapel {
|
||||
flex: 1;
|
||||
font-size:13.5px; font-weight:600; color:var(--ink);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.nilai-chip {
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:13px; font-weight:700;
|
||||
padding:3px 10px;
|
||||
border-radius:var(--r-sm);
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.nilai-chip.a { background:var(--green-soft); color:var(--green); border:1px solid var(--green-border); }
|
||||
.nilai-chip.b { background:var(--blue-soft); color:var(--blue); border:1px solid var(--blue-border); }
|
||||
.nilai-chip.c { background:var(--amber-soft); color:var(--amber); border:1px solid var(--amber-border); }
|
||||
.nilai-chip.d { background:var(--red-soft); color:var(--red); border:1px solid var(--red-border); }
|
||||
|
||||
/* ─── ABSENSI LIST ───────────────────────────────────────── */
|
||||
.absensi-list { padding: 6px 0; flex:1; }
|
||||
|
||||
.absensi-row {
|
||||
display: flex; align-items: center; gap:12px;
|
||||
padding: 13px 22px;
|
||||
border-bottom: 1px solid var(--surface-3);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.absensi-row:last-child { border-bottom:none; }
|
||||
.absensi-row:hover { background: var(--surface-2); }
|
||||
|
||||
.ab-date-box {
|
||||
width:40px; height:40px;
|
||||
border-radius:var(--r-md);
|
||||
background:var(--surface-2);
|
||||
border:1px solid var(--surface-3);
|
||||
display:flex; flex-direction:column;
|
||||
align-items:center; justify-content:center;
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.ab-date-d { font-size:14px; font-weight:700; color:var(--ink); line-height:1; }
|
||||
.ab-date-m { font-size:9px; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:0.5px; margin-top:2px; }
|
||||
|
||||
.ab-info { flex:1; min-width:0; }
|
||||
.ab-mapel { font-size:13px; font-weight:600; color:var(--ink); }
|
||||
.ab-time { font-size:11.5px; color:var(--muted); margin-top:2px; font-family:'JetBrains Mono',monospace; }
|
||||
|
||||
.ab-badge {
|
||||
font-size:11.5px; font-weight:700;
|
||||
padding:4px 11px; border-radius:999px;
|
||||
flex-shrink:0; display:inline-flex; align-items:center; gap:5px;
|
||||
}
|
||||
|
||||
.ab-badge::before {
|
||||
content:''; width:6px; height:6px; border-radius:50%;
|
||||
}
|
||||
|
||||
.ab-badge.hadir { background:var(--green-soft); color:var(--green); border:1px solid var(--green-border); }
|
||||
.ab-badge.hadir::before { background:var(--green); }
|
||||
.ab-badge.izin { background:var(--amber-soft); color:var(--amber); border:1px solid var(--amber-border); }
|
||||
.ab-badge.izin::before { background:var(--amber); }
|
||||
.ab-badge.sakit { background:var(--blue-soft); color:var(--blue); border:1px solid var(--blue-border); }
|
||||
.ab-badge.sakit::before { background:var(--blue); }
|
||||
.ab-badge.alpha { background:var(--red-soft); color:var(--red); border:1px solid var(--red-border); }
|
||||
.ab-badge.alpha::before { background:var(--red); }
|
||||
|
||||
/* ─── EMPTY STATE ────────────────────────────────────────── */
|
||||
.empty-state {
|
||||
padding:42px 24px; text-align:center;
|
||||
}
|
||||
.empty-icon {
|
||||
width:52px; height:52px;
|
||||
background:var(--surface-2); border:1px solid var(--surface-3);
|
||||
border-radius:50%; display:flex; align-items:center; justify-content:center;
|
||||
margin:0 auto 12px;
|
||||
}
|
||||
.empty-icon svg { width:23px; height:23px; color:var(--muted); }
|
||||
.empty-text { font-size:13px; color:var(--muted); font-weight:500; }
|
||||
|
||||
/* ─── RATA NILAI PROGRESS ────────────────────────────────── */
|
||||
.avg-band {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--surface-3);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 22px 24px;
|
||||
margin-bottom: 18px;
|
||||
display: flex; align-items: center; gap: 24px;
|
||||
}
|
||||
|
||||
.avg-circle {
|
||||
width:88px; height:88px;
|
||||
border-radius:50%; flex-shrink:0;
|
||||
background: conic-gradient(
|
||||
var(--blue) calc(var(--pct) * 1%),
|
||||
var(--surface-3) 0
|
||||
);
|
||||
display:flex; align-items:center; justify-content:center;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.avg-circle::before {
|
||||
content:'';
|
||||
position:absolute;
|
||||
width:68px; height:68px;
|
||||
border-radius:50%;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.avg-circle-val {
|
||||
position:relative; z-index:1;
|
||||
font-size:20px; font-weight:800;
|
||||
color:var(--ink); font-family:'JetBrains Mono',monospace;
|
||||
letter-spacing:-0.5px;
|
||||
}
|
||||
|
||||
.avg-info { flex:1; }
|
||||
.avg-label { font-size:12px; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:0.7px; margin-bottom:6px; }
|
||||
.avg-title { font-size:19px; font-weight:800; color:var(--ink); letter-spacing:-0.4px; margin-bottom:8px; }
|
||||
|
||||
.avg-bar-wrap { background:var(--surface-3); border-radius:999px; height:7px; overflow:hidden; }
|
||||
.avg-bar {
|
||||
height:100%; border-radius:999px;
|
||||
background: linear-gradient(90deg, var(--blue), #60a5fa);
|
||||
width: calc(var(--pct) * 1%);
|
||||
transition: width 1.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.avg-foot { display:flex; align-items:center; justify-content:space-between; margin-top:6px; }
|
||||
.avg-foot-text { font-size:12px; color:var(--muted); }
|
||||
.avg-foot-pct { font-size:12px; font-weight:700; color:var(--blue); font-family:'JetBrains Mono',monospace; }
|
||||
|
||||
/* ─── RESPONSIVE ─────────────────────────────────────────── */
|
||||
@media(max-width:1024px) {
|
||||
.stats-grid { grid-template-columns:repeat(2,1fr); }
|
||||
}
|
||||
|
||||
@media(max-width:768px) {
|
||||
.db { padding:16px 16px 40px; }
|
||||
.greeting-band { grid-template-columns:1fr; }
|
||||
.greeting-date-box { display:none; }
|
||||
.greeting-name { font-size:22px; }
|
||||
.stats-grid { grid-template-columns:repeat(2,1fr); }
|
||||
.main-cols { grid-template-columns:1fr; }
|
||||
}
|
||||
|
||||
@media(max-width:480px) {
|
||||
.stats-grid { grid-template-columns:1fr 1fr; gap:10px; }
|
||||
.stat-value { font-size:24px; }
|
||||
}
|
||||
|
||||
/* ─── ANIMATION ──────────────────────────────────────────── */
|
||||
.db > * { animation: rise 0.5s ease both; }
|
||||
.db > *:nth-child(1) { animation-delay:0s; }
|
||||
.db > *:nth-child(2) { animation-delay:0.07s; }
|
||||
.db > *:nth-child(3) { animation-delay:0.14s; }
|
||||
.db > *:nth-child(4) { animation-delay:0.20s; }
|
||||
|
||||
@keyframes rise {
|
||||
from { opacity:0; transform:translateY(16px); }
|
||||
to { opacity:1; transform:translateY(0); }
|
||||
}
|
||||
|
||||
.stats-grid .stat-card { animation: rise 0.5s ease both; }
|
||||
.stats-grid .stat-card:nth-child(1) { animation-delay:0.08s; }
|
||||
.stats-grid .stat-card:nth-child(2) { animation-delay:0.14s; }
|
||||
.stats-grid .stat-card:nth-child(3) { animation-delay:0.20s; }
|
||||
.stats-grid .stat-card:nth-child(4) { animation-delay:0.26s; }
|
||||
</style>
|
||||
|
||||
<div class="db">
|
||||
|
||||
{{-- ── GREETING BAND ────────────────────────────── --}}
|
||||
<div class="greeting-band">
|
||||
<div class="greeting-text">
|
||||
<div class="greeting-tag">
|
||||
<span></span> Portal Akademik Siswa
|
||||
</div>
|
||||
<h1 class="greeting-name">
|
||||
Selamat datang,<br>
|
||||
<em>{{ auth()->user()->name ?? 'Siswa' }}</em>
|
||||
</h1>
|
||||
<p class="greeting-sub">Pantau nilai, absensi, dan progres belajarmu hari ini.</p>
|
||||
</div>
|
||||
<div class="greeting-date-box">
|
||||
<div class="date-day" id="clockDay">—</div>
|
||||
<div class="date-month" id="clockMonth">—</div>
|
||||
<div class="date-time" id="clockTime">00:00:00</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{-- ── STATS ROW ─────────────────────────────────── --}}
|
||||
<div class="stats-grid">
|
||||
|
||||
{{-- Rata-rata Nilai --}}
|
||||
<div class="stat-card blue">
|
||||
<div class="stat-icon-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-label">Rata-rata Nilai</div>
|
||||
<div class="stat-value mono">{{ $rataNilai ? number_format($rataNilai, 1) : '—' }}</div>
|
||||
<div class="stat-sub">dari 100 poin</div>
|
||||
</div>
|
||||
|
||||
{{-- Hadir --}}
|
||||
<div class="stat-card green">
|
||||
<div class="stat-icon-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-label">Total Hadir</div>
|
||||
<div class="stat-value">{{ $hadir }}</div>
|
||||
<div class="stat-sub">pertemuan tercatat</div>
|
||||
</div>
|
||||
|
||||
{{-- Tugas --}}
|
||||
<div class="stat-card amber">
|
||||
<div class="stat-icon-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-label">Tugas</div>
|
||||
<div class="stat-value">{{ $tugas }}</div>
|
||||
<div class="stat-sub">tugas diberikan</div>
|
||||
</div>
|
||||
|
||||
{{-- Mata Pelajaran --}}
|
||||
<div class="stat-card violet">
|
||||
<div class="stat-icon-wrap">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-label">Mata Pelajaran</div>
|
||||
<div class="stat-value">{{ $nilaiTerbaru->pluck('mapel_id')->unique()->count() }}</div>
|
||||
<div class="stat-sub">mapel dinilai</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- ── RATA NILAI BAR ────────────────────────────── --}}
|
||||
@php $pct = $rataNilai ? min(round($rataNilai), 100) : 0; @endphp
|
||||
<div class="avg-band" style="--pct:{{ $pct }};">
|
||||
<div class="avg-circle" style="--pct:{{ $pct }};">
|
||||
<span class="avg-circle-val">{{ $pct }}%</span>
|
||||
</div>
|
||||
<div class="avg-info">
|
||||
<div class="avg-label">Progres Akademik</div>
|
||||
<div class="avg-title">
|
||||
@if($pct >= 85) Prestasi Sangat Baik ⭐
|
||||
@elseif($pct >= 70) Performa Baik 👍
|
||||
@elseif($pct >= 55) Cukup, Terus Semangat 💪
|
||||
@else Butuh Peningkatan 📚
|
||||
@endif
|
||||
</div>
|
||||
<div class="avg-bar-wrap">
|
||||
<div class="avg-bar"></div>
|
||||
</div>
|
||||
<div class="avg-foot">
|
||||
<span class="avg-foot-text">Rata-rata dari semua mata pelajaran</span>
|
||||
<span class="avg-foot-pct">{{ $rataNilai ? number_format($rataNilai, 1) : '0' }} / 100</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── 2 COLUMNS ─────────────────────────────────── --}}
|
||||
<div class="main-cols">
|
||||
|
||||
{{-- NILAI TERBARU --}}
|
||||
<div class="section-card">
|
||||
<div class="sc-head">
|
||||
<div class="sc-head-left">
|
||||
<div class="sc-icon blue">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="sc-title">Nilai Terbaru</span>
|
||||
</div>
|
||||
<span class="sc-badge">5 terkini</span>
|
||||
</div>
|
||||
<div class="nilai-list">
|
||||
@forelse($nilaiTerbaru as $n)
|
||||
@php
|
||||
$v = $n->nilai;
|
||||
$cls = $v >= 85 ? 'a' : ($v >= 70 ? 'b' : ($v >= 55 ? 'c' : 'd'));
|
||||
$colors = ['a'=>'#059669','b'=>'#2563eb','c'=>'#d97706','d'=>'#dc2626'];
|
||||
@endphp
|
||||
<div class="nilai-row">
|
||||
<div class="nilai-mapel-dot" style="background:{{ $colors[$cls] }};"></div>
|
||||
<span class="nilai-mapel">{{ $n->mapel->nama_mapel ?? '—' }}</span>
|
||||
<span class="nilai-chip {{ $cls }}">{{ $v }}</span>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="empty-text">Belum ada data nilai</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ABSENSI TERAKHIR --}}
|
||||
<div class="section-card">
|
||||
<div class="sc-head">
|
||||
<div class="sc-head-left">
|
||||
<div class="sc-icon green">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="sc-title">Riwayat Absensi</span>
|
||||
</div>
|
||||
<span class="sc-badge">5 terkini</span>
|
||||
</div>
|
||||
<div class="absensi-list">
|
||||
@forelse($absensiTerakhir as $a)
|
||||
@php
|
||||
$tgl = \Carbon\Carbon::parse($a->created_at);
|
||||
$statusClass = in_array($a->status, ['hadir','izin','sakit','alpha']) ? $a->status : 'hadir';
|
||||
$statusLabel = ucfirst($a->status);
|
||||
@endphp
|
||||
<div class="absensi-row">
|
||||
<div class="ab-date-box">
|
||||
<div class="ab-date-d">{{ $tgl->format('d') }}</div>
|
||||
<div class="ab-date-m">{{ $tgl->format('M') }}</div>
|
||||
</div>
|
||||
<div class="ab-info">
|
||||
<div class="ab-mapel">Absensi Kelas</div>
|
||||
<div class="ab-time">{{ $tgl->format('H:i') }} WIB</div>
|
||||
</div>
|
||||
<span class="ab-badge {{ $statusClass }}">{{ $statusLabel }}</span>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="empty-text">Belum ada data absensi</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>{{-- /.main-cols --}}
|
||||
|
||||
</div>{{-- /.db --}}
|
||||
|
||||
<script>
|
||||
// Live clock
|
||||
function tick() {
|
||||
const now = new Date();
|
||||
const days = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'];
|
||||
const months = ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'];
|
||||
document.getElementById('clockDay').textContent = String(now.getDate()).padStart(2,'0');
|
||||
document.getElementById('clockMonth').textContent = months[now.getMonth()] + ' ' + now.getFullYear();
|
||||
document.getElementById('clockTime').textContent =
|
||||
String(now.getHours()).padStart(2,'0') + ':' +
|
||||
String(now.getMinutes()).padStart(2,'0') + ':' +
|
||||
String(now.getSeconds()).padStart(2,'0');
|
||||
}
|
||||
tick(); setInterval(tick, 1000);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,38 +1,586 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h3>Jadwal Siswa</h3>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
@if($jadwal->isEmpty())
|
||||
<div class="alert alert-warning">
|
||||
Jadwal belum tersedia
|
||||
:root {
|
||||
--ink: #0c0f1a;
|
||||
--text-sec: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f6f8fc;
|
||||
--surface-3: #eef1f8;
|
||||
--border: #e2e8f0;
|
||||
|
||||
--blue: #2563eb;
|
||||
--blue-dark: #1d4ed8;
|
||||
--blue-soft: #eff6ff;
|
||||
--blue-border: #bfdbfe;
|
||||
|
||||
--green: #059669;
|
||||
--green-soft: #ecfdf5;
|
||||
--green-border:#a7f3d0;
|
||||
|
||||
--violet: #7c3aed;
|
||||
--violet-soft: #f5f3ff;
|
||||
--violet-border:#ddd6fe;
|
||||
|
||||
--amber: #d97706;
|
||||
--amber-soft: #fffbeb;
|
||||
--amber-border:#fde68a;
|
||||
|
||||
--r-md: 10px;
|
||||
--r-lg: 16px;
|
||||
--r-xl: 22px;
|
||||
--r-2xl: 28px;
|
||||
|
||||
--shadow-sm: 0 1px 4px rgba(12,15,26,0.05);
|
||||
--shadow-card: 0 4px 20px rgba(12,15,26,0.07), 0 1px 4px rgba(12,15,26,0.04);
|
||||
--shadow-hover:0 10px 32px rgba(12,15,26,0.11), 0 2px 8px rgba(12,15,26,0.05);
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
.jadwal-page {
|
||||
font-family: 'Sora', sans-serif;
|
||||
padding: 28px 28px 52px;
|
||||
background: var(--surface-2);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── PAGE HEADER ─────────────────────────────────── */
|
||||
.page-hero {
|
||||
background: var(--ink);
|
||||
border-radius: var(--r-2xl);
|
||||
padding: 28px 32px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-hero::before {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 55% 90% at 100% 50%, rgba(37,99,235,0.20) 0%, transparent 65%),
|
||||
radial-gradient(ellipse 35% 60% at 0% 0%, rgba(124,58,237,0.12) 0%, transparent 55%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-left { position: relative; z-index: 1; }
|
||||
|
||||
.hero-tag {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border: 1px solid rgba(255,255,255,0.13);
|
||||
border-radius: 999px;
|
||||
padding: 4px 14px;
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
color: rgba(255,255,255,0.65);
|
||||
letter-spacing: 0.6px; text-transform: uppercase;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero-tag-dot {
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 7px #22c55e;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 24px; font-weight: 800;
|
||||
color: #fff; letter-spacing: -0.4px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero-title em { font-style: normal; color: #93c5fd; }
|
||||
|
||||
.hero-sub {
|
||||
font-size: 13px; color: rgba(255,255,255,0.48);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.hero-right {
|
||||
position: relative; z-index: 1;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hero-stat {
|
||||
background: rgba(255,255,255,0.07);
|
||||
border: 1px solid rgba(255,255,255,0.11);
|
||||
border-radius: var(--r-lg);
|
||||
padding: 14px 20px;
|
||||
text-align: center;
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.hero-stat-val {
|
||||
font-size: 28px; font-weight: 800; color: #fff;
|
||||
letter-spacing: -1px; line-height: 1;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
|
||||
.hero-stat-lbl {
|
||||
font-size: 11px; font-weight: 600;
|
||||
color: rgba(255,255,255,0.45);
|
||||
text-transform: uppercase; letter-spacing: 0.7px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ── HARI FILTER TABS ────────────────────────────── */
|
||||
.hari-tabs {
|
||||
display: flex; gap: 8px; flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hari-tab {
|
||||
padding: 7px 16px;
|
||||
font-size: 12.5px; font-weight: 600;
|
||||
font-family: 'Sora', sans-serif;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text-sec);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.hari-tab:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-soft); }
|
||||
.hari-tab.active { background: var(--blue); color: #fff; border-color: var(--blue); box-shadow: 0 2px 10px rgba(37,99,235,0.3); }
|
||||
|
||||
/* ── EMPTY STATE ─────────────────────────────────── */
|
||||
.empty-wrap {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
padding: 64px 24px;
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 60px; height: 60px;
|
||||
background: var(--surface-2); border: 1px solid var(--border);
|
||||
border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
}
|
||||
|
||||
.empty-icon svg { width: 26px; height: 26px; color: var(--text-muted); }
|
||||
.empty-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
|
||||
.empty-desc { font-size: 13px; color: var(--text-muted); }
|
||||
|
||||
/* ── DESKTOP TABLE ───────────────────────────────── */
|
||||
.table-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 24px 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
flex-wrap: wrap; gap: 12px;
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
font-size: 14px; font-weight: 700; color: var(--ink);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
width: 34px; height: 34px;
|
||||
border-radius: var(--r-md);
|
||||
background: var(--blue-soft); border: 1px solid var(--blue-border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
.toolbar-icon svg { width: 16px; height: 16px; color: var(--blue); }
|
||||
|
||||
.toolbar-badge {
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
background: var(--surface-3); border: 1px solid var(--border);
|
||||
color: var(--text-muted); border-radius: 999px;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.table-responsive { overflow-x: auto; }
|
||||
|
||||
table.jadwal-table {
|
||||
width: 100%; border-collapse: collapse;
|
||||
min-width: 580px;
|
||||
}
|
||||
|
||||
.jadwal-table thead tr {
|
||||
background: var(--surface-2);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.jadwal-table thead th {
|
||||
padding: 12px 18px;
|
||||
font-size: 11px; font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase; letter-spacing: 0.8px;
|
||||
text-align: left; white-space: nowrap;
|
||||
}
|
||||
|
||||
.jadwal-table thead th.center { text-align: center; }
|
||||
|
||||
.jadwal-table tbody tr {
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.jadwal-table tbody tr:last-child { border-bottom: none; }
|
||||
.jadwal-table tbody tr:hover { background: #f8faff; }
|
||||
|
||||
.jadwal-table tbody td {
|
||||
padding: 15px 18px;
|
||||
font-size: 13.5px; color: var(--ink);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.jadwal-table tbody td.center { text-align: center; }
|
||||
|
||||
/* NO CELL */
|
||||
.cell-no {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 13px; color: var(--text-muted);
|
||||
font-weight: 500; width: 50px;
|
||||
}
|
||||
|
||||
/* HARI BADGE */
|
||||
@php
|
||||
$hariColor = [
|
||||
'Senin' => ['bg'=>'#eff6ff','color'=>'#2563eb','border'=>'#bfdbfe'],
|
||||
'Selasa' => ['bg'=>'#f5f3ff','color'=>'#7c3aed','border'=>'#ddd6fe'],
|
||||
'Rabu' => ['bg'=>'#ecfdf5','color'=>'#059669','border'=>'#a7f3d0'],
|
||||
'Kamis' => ['bg'=>'#fffbeb','color'=>'#d97706','border'=>'#fde68a'],
|
||||
'Jumat' => ['bg'=>'#fef2f2','color'=>'#dc2626','border'=>'#fecaca'],
|
||||
'Sabtu' => ['bg'=>'#fff7ed','color'=>'#ea580c','border'=>'#fed7aa'],
|
||||
];
|
||||
@endphp
|
||||
|
||||
.badge-hari {
|
||||
display: inline-flex; align-items: center;
|
||||
border-radius: 999px;
|
||||
font-size: 12px; font-weight: 700;
|
||||
padding: 4px 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* MAPEL */
|
||||
.mapel-cell {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
|
||||
.mapel-dot {
|
||||
width: 8px; height: 8px; border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
background: var(--blue);
|
||||
}
|
||||
|
||||
.mapel-name {
|
||||
font-weight: 600; color: var(--ink);
|
||||
}
|
||||
|
||||
/* GURU */
|
||||
.guru-pill {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: var(--surface-2); border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 5px 13px;
|
||||
font-size: 12.5px; color: var(--text-sec); font-weight: 500;
|
||||
}
|
||||
|
||||
.guru-avatar {
|
||||
width: 22px; height: 22px; border-radius: 50%;
|
||||
background: var(--blue-soft); border: 1px solid var(--blue-border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 9px; font-weight: 700; color: var(--blue);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* JAM */
|
||||
.jam-pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--green-soft); border: 1px solid var(--green-border);
|
||||
border-radius: 999px;
|
||||
padding: 5px 13px;
|
||||
font-size: 12.5px; font-weight: 700; color: var(--green);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jam-pill svg { width: 13px; height: 13px; }
|
||||
|
||||
/* TABLE FOOTER */
|
||||
.table-footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 13px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-2);
|
||||
font-size: 12.5px; color: var(--text-muted);
|
||||
flex-wrap: wrap; gap: 6px;
|
||||
}
|
||||
|
||||
/* ── MOBILE CARDS ─────────────────────────────────── */
|
||||
.mobile-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
.m-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: 18px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
}
|
||||
|
||||
.m-card:hover {
|
||||
box-shadow: var(--shadow-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.m-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0; top: 0; bottom: 0;
|
||||
width: 4px;
|
||||
background: var(--blue);
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.m-card-top {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.m-card-mapel {
|
||||
font-size: 15px; font-weight: 700; color: var(--ink);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.m-card-meta {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* ── ANIMATION ────────────────────────────────────── */
|
||||
.jadwal-page > * { animation: rise 0.45s ease both; }
|
||||
.jadwal-page > *:nth-child(1) { animation-delay: 0s; }
|
||||
.jadwal-page > *:nth-child(2) { animation-delay: 0.06s; }
|
||||
.jadwal-page > *:nth-child(3) { animation-delay: 0.12s; }
|
||||
.jadwal-page > *:nth-child(4) { animation-delay: 0.18s; }
|
||||
|
||||
@keyframes rise {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ── RESPONSIVE ───────────────────────────────────── */
|
||||
@media (max-width: 768px) {
|
||||
.jadwal-page { padding: 16px 16px 48px; }
|
||||
.page-hero { padding: 22px 20px; }
|
||||
.hero-title { font-size: 20px; }
|
||||
.hero-right { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="jadwal-page">
|
||||
|
||||
{{-- ── HERO ──────────────────────────────────────── --}}
|
||||
<div class="page-hero">
|
||||
<div class="hero-left">
|
||||
<div class="hero-tag">
|
||||
<span class="hero-tag-dot"></span>
|
||||
Akademik Siswa
|
||||
</div>
|
||||
<h1 class="hero-title">Jadwal <em>Pelajaran</em></h1>
|
||||
<p class="hero-sub">Semua jadwal mata pelajaran kamu tersedia di sini</p>
|
||||
</div>
|
||||
<div class="hero-right">
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ $jadwal->count() }}</div>
|
||||
<div class="hero-stat-lbl">Total Sesi</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ $jadwal->unique('hari')->count() }}</div>
|
||||
<div class="hero-stat-lbl">Hari Aktif</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Hari</th>
|
||||
<th>Mapel</th>
|
||||
<th>Guru</th>
|
||||
<th>Jam</th>
|
||||
</tr>
|
||||
{{-- ── HARI FILTER TABS ───────────────────────────── --}}
|
||||
@if(!$jadwal->isEmpty())
|
||||
<div class="hari-tabs">
|
||||
<div class="hari-tab active" onclick="filterHari('semua', this)">Semua</div>
|
||||
@foreach($jadwal->unique('hari') as $jh)
|
||||
<div class="hari-tab" onclick="filterHari('{{ $jh->hari }}', this)">{{ $jh->hari }}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@foreach($jadwal as $j)
|
||||
<tr>
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
<td>{{ $j->hari }}</td>
|
||||
<td>{{ $j->mapel->nama_mapel ?? '-' }}</td>
|
||||
<td>{{ $j->guru->nama ?? '-' }}</td>
|
||||
<td>
|
||||
{{ $j->jam_mulai }} - {{ $j->jam_selesai }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
{{-- ── EMPTY STATE ────────────────────────────────── --}}
|
||||
@if($jadwal->isEmpty())
|
||||
<div class="empty-wrap">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Jadwal Belum Tersedia</div>
|
||||
<div class="empty-desc">Hubungi wali kelas atau admin untuk informasi jadwal.</div>
|
||||
</div>
|
||||
@else
|
||||
|
||||
</table>
|
||||
{{-- ── DESKTOP TABLE ──────────────────────────────── --}}
|
||||
<div class="table-card d-none d-md-block" id="desktopTable">
|
||||
<div class="table-toolbar">
|
||||
<div class="toolbar-title">
|
||||
<div class="toolbar-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
Daftar Jadwal
|
||||
<span class="toolbar-badge">{{ $jadwal->count() }} sesi</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="jadwal-table" id="jadwalTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Hari</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Guru</th>
|
||||
<th class="center">Jam Mengajar</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($jadwal as $j)
|
||||
@php
|
||||
$hc = $hariColor[$j->hari] ?? ['bg'=>'#f1f5f9','color'=>'#64748b','border'=>'#e2e8f0'];
|
||||
$inisial = strtoupper(substr($j->guru->nama ?? 'G', 0, 2));
|
||||
@endphp
|
||||
<tr data-hari="{{ $j->hari }}">
|
||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
||||
<td>
|
||||
<span class="badge-hari"
|
||||
style="background:{{ $hc['bg'] }};color:{{ $hc['color'] }};border:1px solid {{ $hc['border'] }};">
|
||||
{{ $j->hari }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="mapel-cell">
|
||||
<div class="mapel-dot" style="background:{{ $hc['color'] }};"></div>
|
||||
<span class="mapel-name">{{ $j->mapel->nama_mapel ?? '—' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="guru-pill">
|
||||
<div class="guru-avatar">{{ $inisial }}</div>
|
||||
{{ $j->guru->nama ?? '—' }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="center">
|
||||
<span class="jam-pill">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
{{ $j->jam_mulai }} – {{ $j->jam_selesai }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-footer">
|
||||
<span id="footerCount">Menampilkan {{ $jadwal->count() }} jadwal</span>
|
||||
<span>{{ now()->format('l, d F Y') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── MOBILE CARDS ────────────────────────────────── --}}
|
||||
<div class="mobile-list d-block d-md-none" id="mobileList">
|
||||
@foreach($jadwal as $j)
|
||||
@php
|
||||
$hc = $hariColor[$j->hari] ?? ['bg'=>'#f1f5f9','color'=>'#64748b','border'=>'#e2e8f0'];
|
||||
$inisial = strtoupper(substr($j->guru->nama ?? 'G', 0, 2));
|
||||
@endphp
|
||||
<div class="m-card" data-hari="{{ $j->hari }}"
|
||||
style="--accent: {{ $hc['color'] }};">
|
||||
<style>.m-card[data-hari="{{ $j->hari }}"]:first-of-type::before { background: {{ $hc['color'] }}; }</style>
|
||||
|
||||
<div class="m-card-top">
|
||||
<span class="badge-hari"
|
||||
style="background:{{ $hc['bg'] }};color:{{ $hc['color'] }};border:1px solid {{ $hc['border'] }};">
|
||||
{{ $j->hari }}
|
||||
</span>
|
||||
<span class="jam-pill" style="font-size:12px;padding:4px 11px;">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="width:12px;height:12px;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
{{ $j->jam_mulai }} – {{ $j->jam_selesai }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="m-card-mapel">{{ $j->mapel->nama_mapel ?? '—' }}</div>
|
||||
|
||||
<div class="m-card-meta">
|
||||
<div class="guru-pill" style="font-size:12px;">
|
||||
<div class="guru-avatar">{{ $inisial }}</div>
|
||||
{{ $j->guru->nama ?? '—' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@endif {{-- end if not empty --}}
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function filterHari(hari, el) {
|
||||
// update tab active
|
||||
document.querySelectorAll('.hari-tab').forEach(t => t.classList.remove('active'));
|
||||
el.classList.add('active');
|
||||
|
||||
// filter desktop rows
|
||||
document.querySelectorAll('#jadwalTable tbody tr').forEach(row => {
|
||||
row.style.display = (hari === 'semua' || row.dataset.hari === hari) ? '' : 'none';
|
||||
});
|
||||
|
||||
// filter mobile cards
|
||||
document.querySelectorAll('#mobileList .m-card').forEach(card => {
|
||||
card.style.display = (hari === 'semua' || card.dataset.hari === hari) ? '' : 'none';
|
||||
});
|
||||
|
||||
// update footer count
|
||||
const visible = [...document.querySelectorAll('#jadwalTable tbody tr')]
|
||||
.filter(r => r.style.display !== 'none').length;
|
||||
const el2 = document.getElementById('footerCount');
|
||||
if (el2) el2.textContent = 'Menampilkan ' + visible + ' jadwal';
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,26 +1,585 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h3>Data Nilai</h3>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Mapel</th>
|
||||
<th>Nilai</th>
|
||||
</tr>
|
||||
:root {
|
||||
--ink: #0c0f1a;
|
||||
--text-sec: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f6f8fc;
|
||||
--surface-3: #eef1f8;
|
||||
--border: #e2e8f0;
|
||||
|
||||
@foreach($nilai as $n)
|
||||
<tr>
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
<td>{{ $n->mapel->nama_mapel }}</td>
|
||||
<td>{{ $n->nilai }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
--blue: #2563eb;
|
||||
--blue-dark: #1d4ed8;
|
||||
--blue-soft: #eff6ff;
|
||||
--blue-border: #bfdbfe;
|
||||
|
||||
</table>
|
||||
--green: #059669;
|
||||
--green-soft: #ecfdf5;
|
||||
--green-border: #a7f3d0;
|
||||
|
||||
--amber: #d97706;
|
||||
--amber-soft: #fffbeb;
|
||||
--amber-border: #fde68a;
|
||||
|
||||
--violet: #7c3aed;
|
||||
--violet-soft: #f5f3ff;
|
||||
--violet-border:#ddd6fe;
|
||||
|
||||
--red: #dc2626;
|
||||
--red-soft: #fef2f2;
|
||||
--red-border: #fecaca;
|
||||
|
||||
--r-md: 10px;
|
||||
--r-lg: 16px;
|
||||
--r-xl: 22px;
|
||||
--r-2xl: 28px;
|
||||
|
||||
--shadow-card: 0 4px 20px rgba(12,15,26,0.07), 0 1px 4px rgba(12,15,26,0.04);
|
||||
--shadow-hover:0 10px 32px rgba(12,15,26,0.11), 0 2px 8px rgba(12,15,26,0.05);
|
||||
}
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
.nilai-page {
|
||||
font-family: 'Sora', sans-serif;
|
||||
padding: 28px 28px 52px;
|
||||
background: var(--surface-2);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── ANIMATIONS ───────────────────────────────────── */
|
||||
.nilai-page > * { animation: rise 0.45s ease both; }
|
||||
.nilai-page > *:nth-child(1) { animation-delay: 0s; }
|
||||
.nilai-page > *:nth-child(2) { animation-delay: 0.07s; }
|
||||
.nilai-page > *:nth-child(3) { animation-delay: 0.14s; }
|
||||
.nilai-page > *:nth-child(4) { animation-delay: 0.20s; }
|
||||
|
||||
@keyframes rise {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ── HERO BANNER ──────────────────────────────────── */
|
||||
.page-hero {
|
||||
background: var(--ink);
|
||||
border-radius: var(--r-2xl);
|
||||
padding: 28px 32px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-hero::before {
|
||||
content: '';
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 55% 90% at 100% 50%, rgba(37,99,235,0.22) 0%, transparent 65%),
|
||||
radial-gradient(ellipse 35% 60% at 0% 0%, rgba(124,58,237,0.13) 0%, transparent 55%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-left { position: relative; z-index: 1; }
|
||||
|
||||
.hero-tag {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border: 1px solid rgba(255,255,255,0.13);
|
||||
border-radius: 999px;
|
||||
padding: 4px 14px;
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
color: rgba(255,255,255,0.6);
|
||||
letter-spacing: 0.6px; text-transform: uppercase;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero-tag-dot {
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 7px #22c55e;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 24px; font-weight: 800;
|
||||
color: #fff; letter-spacing: -0.4px; line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero-title em { font-style: normal; color: #93c5fd; }
|
||||
|
||||
.hero-sub {
|
||||
font-size: 13px; color: rgba(255,255,255,0.45);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.hero-right {
|
||||
position: relative; z-index: 1;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hero-stat {
|
||||
background: rgba(255,255,255,0.07);
|
||||
border: 1px solid rgba(255,255,255,0.11);
|
||||
border-radius: var(--r-lg);
|
||||
padding: 14px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-stat-val {
|
||||
font-size: 28px; font-weight: 800; color: #fff;
|
||||
letter-spacing: -1px; line-height: 1;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
|
||||
.hero-stat-lbl {
|
||||
font-size: 11px; font-weight: 600;
|
||||
color: rgba(255,255,255,0.42);
|
||||
text-transform: uppercase; letter-spacing: 0.7px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ── SUMMARY CARDS ────────────────────────────────── */
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.sum-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
padding: 20px 18px;
|
||||
box-shadow: var(--shadow-card);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
}
|
||||
|
||||
.sum-card:hover {
|
||||
box-shadow: var(--shadow-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sum-card::before {
|
||||
content: '';
|
||||
position: absolute; top: 0; left: 0; right: 0;
|
||||
height: 3px; border-radius: var(--r-xl) var(--r-xl) 0 0;
|
||||
}
|
||||
|
||||
.sum-card.blue::before { background: linear-gradient(90deg, var(--blue), #60a5fa); }
|
||||
.sum-card.amber::before { background: linear-gradient(90deg, var(--amber), #fbbf24); }
|
||||
.sum-card.green::before { background: linear-gradient(90deg, var(--green), #34d399); }
|
||||
.sum-card.violet::before { background: linear-gradient(90deg, var(--violet), #a78bfa); }
|
||||
|
||||
.sum-icon {
|
||||
width: 40px; height: 40px;
|
||||
border-radius: var(--r-md);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.sum-card.blue .sum-icon { background: var(--blue-soft); border: 1px solid var(--blue-border); }
|
||||
.sum-card.amber .sum-icon { background: var(--amber-soft); border: 1px solid var(--amber-border); }
|
||||
.sum-card.green .sum-icon { background: var(--green-soft); border: 1px solid var(--green-border); }
|
||||
.sum-card.violet .sum-icon { background: var(--violet-soft); border: 1px solid var(--violet-border); }
|
||||
|
||||
.sum-icon svg { width: 18px; height: 18px; }
|
||||
.sum-card.blue .sum-icon svg { color: var(--blue); }
|
||||
.sum-card.amber .sum-icon svg { color: var(--amber); }
|
||||
.sum-card.green .sum-icon svg { color: var(--green); }
|
||||
.sum-card.violet .sum-icon svg { color: var(--violet); }
|
||||
|
||||
.sum-label {
|
||||
font-size: 11px; font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase; letter-spacing: 0.7px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.sum-value {
|
||||
font-size: 26px; font-weight: 800;
|
||||
color: var(--ink); letter-spacing: -0.8px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sum-sub {
|
||||
font-size: 11.5px; color: var(--text-muted);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* ── TABLE CARD ───────────────────────────────────── */
|
||||
.table-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 24px 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-wrap: wrap; gap: 12px;
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
font-size: 14px; font-weight: 700; color: var(--ink);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
width: 34px; height: 34px;
|
||||
border-radius: var(--r-md);
|
||||
background: var(--blue-soft); border: 1px solid var(--blue-border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
.toolbar-icon svg { width: 16px; height: 16px; color: var(--blue); }
|
||||
|
||||
.toolbar-badge {
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
background: var(--surface-3); border: 1px solid var(--border);
|
||||
color: var(--text-muted); border-radius: 999px;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
/* ── TABLE ────────────────────────────────────────── */
|
||||
.table-responsive { overflow-x: auto; }
|
||||
|
||||
table.nilai-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 620px;
|
||||
}
|
||||
|
||||
.nilai-table thead tr {
|
||||
background: var(--surface-2);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.nilai-table thead th {
|
||||
padding: 12px 18px;
|
||||
font-size: 11px; font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase; letter-spacing: 0.8px;
|
||||
text-align: left; white-space: nowrap;
|
||||
}
|
||||
|
||||
.nilai-table thead th.center { text-align: center; }
|
||||
|
||||
.nilai-table tbody tr {
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.nilai-table tbody tr:last-child { border-bottom: none; }
|
||||
.nilai-table tbody tr:hover { background: #f8faff; }
|
||||
|
||||
.nilai-table tbody td {
|
||||
padding: 15px 18px;
|
||||
font-size: 13.5px; color: var(--ink);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nilai-table tbody td.center { text-align: center; }
|
||||
|
||||
/* NO CELL */
|
||||
.cell-no {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12.5px; color: var(--text-muted);
|
||||
font-weight: 500; width: 50px;
|
||||
}
|
||||
|
||||
/* MAPEL CELL */
|
||||
.mapel-cell {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
|
||||
.mapel-dot {
|
||||
width: 8px; height: 8px; border-radius: 50%;
|
||||
background: var(--blue); flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mapel-name {
|
||||
font-weight: 600; color: var(--ink);
|
||||
}
|
||||
|
||||
/* NILAI CHIPS */
|
||||
.chip {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 52px;
|
||||
padding: 5px 13px;
|
||||
border-radius: var(--r-md);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 13px; font-weight: 700;
|
||||
}
|
||||
|
||||
.chip.tugas { background: var(--blue-soft); color: var(--blue); border: 1px solid var(--blue-border); }
|
||||
.chip.uts { background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber-border); }
|
||||
.chip.uas { background: var(--green-soft); color: var(--green); border: 1px solid var(--green-border); }
|
||||
|
||||
/* RATA CHIP — dynamic color */
|
||||
.chip.rata-a { background: var(--green-soft); color: var(--green); border: 1px solid var(--green-border); }
|
||||
.chip.rata-b { background: var(--blue-soft); color: var(--blue); border: 1px solid var(--blue-border); }
|
||||
.chip.rata-c { background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber-border); }
|
||||
.chip.rata-d { background: var(--red-soft); color: var(--red); border: 1px solid var(--red-border); }
|
||||
|
||||
/* PROGRESS BAR inline */
|
||||
.rata-wrap {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
|
||||
.mini-bar {
|
||||
flex: 1; height: 6px;
|
||||
background: var(--surface-3);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.mini-bar-fill {
|
||||
height: 100%; border-radius: 999px;
|
||||
transition: width 0.8s cubic-bezier(0.16,1,0.3,1);
|
||||
}
|
||||
|
||||
/* EMPTY */
|
||||
.empty-state {
|
||||
padding: 64px 24px; text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 60px; height: 60px;
|
||||
background: var(--surface-2); border: 1px solid var(--border);
|
||||
border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 14px;
|
||||
}
|
||||
|
||||
.empty-icon svg { width: 26px; height: 26px; color: var(--text-muted); }
|
||||
.empty-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
|
||||
.empty-desc { font-size: 13px; color: var(--text-muted); }
|
||||
|
||||
/* TABLE FOOTER */
|
||||
.table-footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 13px 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-2);
|
||||
font-size: 12.5px; color: var(--text-muted);
|
||||
flex-wrap: wrap; gap: 6px;
|
||||
}
|
||||
|
||||
/* ── RESPONSIVE ───────────────────────────────────── */
|
||||
@media (max-width: 1024px) { .summary-grid { grid-template-columns: repeat(2,1fr); } }
|
||||
@media (max-width: 768px) {
|
||||
.nilai-page { padding: 16px 16px 48px; }
|
||||
.page-hero { padding: 22px 20px; }
|
||||
.hero-title { font-size: 20px; }
|
||||
.hero-right { display: none; }
|
||||
.summary-grid { grid-template-columns: repeat(2,1fr); gap: 10px; }
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.summary-grid { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="nilai-page">
|
||||
|
||||
{{-- ── HERO ──────────────────────────────────────── --}}
|
||||
<div class="page-hero">
|
||||
<div class="hero-left">
|
||||
<div class="hero-tag">
|
||||
<span class="hero-tag-dot"></span>
|
||||
Rekap Akademik
|
||||
</div>
|
||||
<h1 class="hero-title">Data <em>Nilai Siswa</em></h1>
|
||||
<p class="hero-sub">Ringkasan nilai tugas, UTS, UAS, dan rata-rata per mata pelajaran</p>
|
||||
</div>
|
||||
<div class="hero-right">
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ count($data) }}</div>
|
||||
<div class="hero-stat-lbl">Mata Pelajaran</div>
|
||||
</div>
|
||||
@php
|
||||
$avgAll = collect($data)->avg('rata');
|
||||
@endphp
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ $avgAll ? number_format($avgAll, 1) : '—' }}</div>
|
||||
<div class="hero-stat-lbl">Rata-rata</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── SUMMARY CARDS ──────────────────────────────── --}}
|
||||
@php
|
||||
$avgTugas = collect($data)->whereNotNull('tugas')->avg('tugas');
|
||||
$avgUts = collect($data)->whereNotNull('uts')->avg('uts');
|
||||
$avgUas = collect($data)->whereNotNull('uas')->avg('uas');
|
||||
$avgRata = collect($data)->whereNotNull('rata')->avg('rata');
|
||||
@endphp
|
||||
|
||||
<div class="summary-grid">
|
||||
<div class="sum-card blue">
|
||||
<div class="sum-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sum-label">Avg. Tugas</div>
|
||||
<div class="sum-value">{{ $avgTugas ? number_format($avgTugas,1) : '—' }}</div>
|
||||
<div class="sum-sub">rata-rata nilai tugas</div>
|
||||
</div>
|
||||
|
||||
<div class="sum-card amber">
|
||||
<div class="sum-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sum-label">Avg. UTS</div>
|
||||
<div class="sum-value">{{ $avgUts ? number_format($avgUts,1) : '—' }}</div>
|
||||
<div class="sum-sub">rata-rata nilai UTS</div>
|
||||
</div>
|
||||
|
||||
<div class="sum-card green">
|
||||
<div class="sum-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sum-label">Avg. UAS</div>
|
||||
<div class="sum-value">{{ $avgUas ? number_format($avgUas,1) : '—' }}</div>
|
||||
<div class="sum-sub">rata-rata nilai UAS</div>
|
||||
</div>
|
||||
|
||||
<div class="sum-card violet">
|
||||
<div class="sum-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sum-label">Avg. Keseluruhan</div>
|
||||
<div class="sum-value">{{ $avgRata ? number_format($avgRata,1) : '—' }}</div>
|
||||
<div class="sum-sub">rata-rata semua mapel</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── TABLE ──────────────────────────────────────── --}}
|
||||
<div class="table-card">
|
||||
<div class="table-toolbar">
|
||||
<div class="toolbar-title">
|
||||
<div class="toolbar-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
Rekap Nilai Per Mapel
|
||||
<span class="toolbar-badge">{{ count($data) }} mapel</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="nilai-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th class="center">Tugas</th>
|
||||
<th class="center">UTS</th>
|
||||
<th class="center">UAS</th>
|
||||
<th>Rata-rata</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($data as $d)
|
||||
@php
|
||||
$rata = $d['rata'] ?? null;
|
||||
$rataClass = !$rata ? 'rata-b'
|
||||
: ($rata >= 85 ? 'rata-a'
|
||||
: ($rata >= 70 ? 'rata-b'
|
||||
: ($rata >= 55 ? 'rata-c' : 'rata-d')));
|
||||
|
||||
$barColor = !$rata ? '#2563eb'
|
||||
: ($rata >= 85 ? '#059669'
|
||||
: ($rata >= 70 ? '#2563eb'
|
||||
: ($rata >= 55 ? '#d97706' : '#dc2626')));
|
||||
|
||||
$pct = $rata ? min($rata, 100) : 0;
|
||||
@endphp
|
||||
<tr>
|
||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
||||
|
||||
<td>
|
||||
<div class="mapel-cell">
|
||||
<div class="mapel-dot" style="background:{{ $barColor }};"></div>
|
||||
<span class="mapel-name">{{ $d['mapel'] ?? '—' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
<span class="chip tugas">{{ $d['tugas'] ?? '—' }}</span>
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
<span class="chip uts">{{ $d['uts'] ?? '—' }}</span>
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
<span class="chip uas">{{ $d['uas'] ?? '—' }}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="rata-wrap">
|
||||
<span class="chip {{ $rataClass }}">{{ $rata ?? '—' }}</span>
|
||||
@if($rata)
|
||||
<div class="mini-bar">
|
||||
<div class="mini-bar-fill"
|
||||
style="width:{{ $pct }}%; background:{{ $barColor }};"></div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum Ada Data Nilai</div>
|
||||
<div class="empty-desc">Nilai akan muncul setelah guru menginput data penilaian.</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if(count($data) > 0)
|
||||
<div class="table-footer">
|
||||
<span>Menampilkan {{ count($data) }} mata pelajaran</span>
|
||||
<span>{{ now()->format('d F Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -1,92 +1,533 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h4>Daftar Tugas</h4>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
<table class="table table-bordered">
|
||||
:root {
|
||||
--ink: #0c0f1a;
|
||||
--text-sec: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f6f8fc;
|
||||
--surface-3: #eef1f8;
|
||||
--border: #e2e8f0;
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Judul</th>
|
||||
<th>Mapel</th>
|
||||
<th>Deadline</th>
|
||||
<th>Waktu Kumpul</th>
|
||||
<th>Nilai</th>
|
||||
<th>Komentar</th>
|
||||
<th>Status</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
--blue: #2563eb;
|
||||
--blue-dark: #1d4ed8;
|
||||
--blue-soft: #eff6ff;
|
||||
--blue-border: #bfdbfe;
|
||||
|
||||
<tbody>
|
||||
@foreach($tugas as $t)
|
||||
<tr>
|
||||
--green: #059669;
|
||||
--green-soft: #ecfdf5;
|
||||
--green-border: #a7f3d0;
|
||||
|
||||
<td>{{ $t->judul }}</td>
|
||||
<td>{{ $t->mapel->nama_mapel ?? '-' }}</td>
|
||||
--amber: #d97706;
|
||||
--amber-soft: #fffbeb;
|
||||
--amber-border: #fde68a;
|
||||
|
||||
<td>
|
||||
{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}
|
||||
</td>
|
||||
--red: #dc2626;
|
||||
--red-soft: #fef2f2;
|
||||
--red-border: #fecaca;
|
||||
|
||||
<td>
|
||||
@if($t->waktu_kumpul)
|
||||
{{ \Carbon\Carbon::parse($t->waktu_kumpul)->format('d M Y H:i') }}
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
--slate: #64748b;
|
||||
--slate-soft: #f1f5f9;
|
||||
--slate-border: #cbd5e1;
|
||||
|
||||
{{-- NILAI --}}
|
||||
<td>
|
||||
{{ $t->nilai ?? '-' }}
|
||||
</td>
|
||||
--r-md: 10px; --r-lg: 16px;
|
||||
--r-xl: 22px; --r-2xl: 28px;
|
||||
|
||||
{{-- KOMENTAR --}}
|
||||
<td>
|
||||
{{ $t->komentar ?? '-' }}
|
||||
</td>
|
||||
--shadow-sm: 0 1px 4px rgba(12,15,26,0.05);
|
||||
--shadow-card: 0 4px 20px rgba(12,15,26,0.07), 0 1px 4px rgba(12,15,26,0.04);
|
||||
--shadow-hover:0 10px 32px rgba(12,15,26,0.11), 0 2px 8px rgba(12,15,26,0.05);
|
||||
}
|
||||
|
||||
{{-- STATUS --}}
|
||||
<td>
|
||||
@if($t->status == 'tepat')
|
||||
<span class="badge bg-success">Tepat</span>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
@elseif($t->status == 'telat')
|
||||
<span class="badge bg-warning text-dark">Telat</span>
|
||||
.tugas-page {
|
||||
font-family: 'Sora', sans-serif;
|
||||
padding: 28px 28px 52px;
|
||||
background: var(--surface-2);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@elseif($t->status == 'lewat')
|
||||
<span class="badge bg-danger">Belum Kumpul</span>
|
||||
/* ANIMATION */
|
||||
.tugas-page > * { animation: rise 0.45s ease both; }
|
||||
.tugas-page > *:nth-child(1) { animation-delay: 0s; }
|
||||
.tugas-page > *:nth-child(2) { animation-delay: 0.07s; }
|
||||
.tugas-page > *:nth-child(3) { animation-delay: 0.13s; }
|
||||
.tugas-page > *:nth-child(4) { animation-delay: 0.19s; }
|
||||
@keyframes rise {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@else
|
||||
<span class="badge bg-secondary">Belum</span>
|
||||
@endif
|
||||
</td>
|
||||
/* ── HERO ─────────────────────────────────────────── */
|
||||
.page-hero {
|
||||
background: var(--ink);
|
||||
border-radius: var(--r-2xl);
|
||||
padding: 28px 32px;
|
||||
margin-bottom: 24px;
|
||||
display: flex; align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px; position: relative; overflow: hidden;
|
||||
}
|
||||
.page-hero::before {
|
||||
content: ''; position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 55% 90% at 100% 50%, rgba(37,99,235,0.22) 0%, transparent 65%),
|
||||
radial-gradient(ellipse 35% 60% at 0% 0%, rgba(124,58,237,0.13) 0%, transparent 55%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.hero-left { position: relative; z-index: 1; }
|
||||
.hero-tag {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.13);
|
||||
border-radius: 999px; padding: 4px 14px;
|
||||
font-size: 11.5px; font-weight: 600; color: rgba(255,255,255,0.6);
|
||||
letter-spacing: 0.6px; text-transform: uppercase; margin-bottom: 12px;
|
||||
}
|
||||
.hero-tag-dot {
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
background: #22c55e; box-shadow: 0 0 7px #22c55e;
|
||||
}
|
||||
.hero-title {
|
||||
font-size: 24px; font-weight: 800; color: #fff;
|
||||
letter-spacing: -0.4px; line-height: 1.2;
|
||||
}
|
||||
.hero-title em { font-style: normal; color: #93c5fd; }
|
||||
.hero-sub { font-size: 13px; color: rgba(255,255,255,0.45); margin-top: 6px; }
|
||||
|
||||
{{-- AKSI --}}
|
||||
<td>
|
||||
@if($t->file)
|
||||
<a href="{{ route('guru.tugas.download', $t->id) }}"
|
||||
class="btn btn-sm btn-success">
|
||||
Download
|
||||
</a>
|
||||
@endif
|
||||
.hero-right {
|
||||
position: relative; z-index: 1;
|
||||
display: flex; align-items: center; gap: 12px; flex-shrink: 0;
|
||||
}
|
||||
.hero-stat {
|
||||
background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.11);
|
||||
border-radius: var(--r-lg); padding: 14px 20px; text-align: center;
|
||||
}
|
||||
.hero-stat-val {
|
||||
font-size: 28px; font-weight: 800; color: #fff;
|
||||
letter-spacing: -1px; line-height: 1;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
.hero-stat-lbl {
|
||||
font-size: 11px; font-weight: 600; color: rgba(255,255,255,0.42);
|
||||
text-transform: uppercase; letter-spacing: 0.7px; margin-top: 4px;
|
||||
}
|
||||
|
||||
@if(!$t->waktu_kumpul && now()->lt($t->deadline))
|
||||
<a href="{{ route('siswa.tugas.kumpul', $t->id) }}"
|
||||
class="btn btn-sm btn-primary">
|
||||
Kumpulkan
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
/* ── STATS ROW ────────────────────────────────────── */
|
||||
.stats-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px; margin-bottom: 22px;
|
||||
}
|
||||
.s-card {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl); padding: 18px 16px;
|
||||
box-shadow: var(--shadow-card);
|
||||
position: relative; overflow: hidden;
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
}
|
||||
.s-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
|
||||
.s-card::before {
|
||||
content: ''; position: absolute; top: 0; left: 0; right: 0;
|
||||
height: 3px; border-radius: var(--r-xl) var(--r-xl) 0 0;
|
||||
}
|
||||
.s-card.blue::before { background: linear-gradient(90deg,#2563eb,#60a5fa); }
|
||||
.s-card.green::before { background: linear-gradient(90deg,#059669,#34d399); }
|
||||
.s-card.amber::before { background: linear-gradient(90deg,#d97706,#fbbf24); }
|
||||
.s-card.red::before { background: linear-gradient(90deg,#dc2626,#f87171); }
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
.s-icon {
|
||||
width: 38px; height: 38px; border-radius: var(--r-md);
|
||||
display: flex; align-items: center; justify-content: center; margin-bottom: 12px;
|
||||
}
|
||||
.s-card.blue .s-icon { background: var(--blue-soft); border: 1px solid var(--blue-border); }
|
||||
.s-card.green .s-icon { background: var(--green-soft); border: 1px solid var(--green-border); }
|
||||
.s-card.amber .s-icon { background: var(--amber-soft); border: 1px solid var(--amber-border); }
|
||||
.s-card.red .s-icon { background: var(--red-soft); border: 1px solid var(--red-border); }
|
||||
.s-icon svg { width: 17px; height: 17px; }
|
||||
.s-card.blue .s-icon svg { color: var(--blue); }
|
||||
.s-card.green .s-icon svg { color: var(--green); }
|
||||
.s-card.amber .s-icon svg { color: var(--amber); }
|
||||
.s-card.red .s-icon svg { color: var(--red); }
|
||||
|
||||
</table>
|
||||
.s-label { font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.7px; margin-bottom: 4px; }
|
||||
.s-value { font-size: 24px; font-weight: 800; color: var(--ink); letter-spacing: -0.5px; font-family: 'JetBrains Mono', monospace; line-height: 1; }
|
||||
.s-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; }
|
||||
|
||||
/* ── TABLE CARD ───────────────────────────────────── */
|
||||
.table-card {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl); box-shadow: var(--shadow-card); overflow: hidden;
|
||||
}
|
||||
.table-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 18px 24px 14px; border-bottom: 1px solid var(--border);
|
||||
flex-wrap: wrap; gap: 12px;
|
||||
}
|
||||
.toolbar-title {
|
||||
font-size: 14px; font-weight: 700; color: var(--ink);
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.toolbar-icon {
|
||||
width: 34px; height: 34px; border-radius: var(--r-md);
|
||||
background: var(--blue-soft); border: 1px solid var(--blue-border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.toolbar-icon svg { width: 16px; height: 16px; color: var(--blue); }
|
||||
.toolbar-badge {
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
background: var(--surface-3); border: 1px solid var(--border);
|
||||
color: var(--text-muted); border-radius: 999px; padding: 2px 10px;
|
||||
}
|
||||
|
||||
/* ── TABLE ────────────────────────────────────────── */
|
||||
.table-responsive { overflow-x: auto; }
|
||||
table.tugas-table { width: 100%; border-collapse: collapse; min-width: 720px; }
|
||||
.tugas-table thead tr { background: var(--surface-2); border-bottom: 1px solid var(--border); }
|
||||
.tugas-table thead th {
|
||||
padding: 12px 18px; font-size: 11px; font-weight: 700;
|
||||
color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.8px;
|
||||
text-align: left; white-space: nowrap;
|
||||
}
|
||||
.tugas-table thead th.center { text-align: center; }
|
||||
.tugas-table tbody tr { border-bottom: 1px solid var(--border); transition: background 0.15s; }
|
||||
.tugas-table tbody tr:last-child { border-bottom: none; }
|
||||
.tugas-table tbody tr:hover { background: #f8faff; }
|
||||
.tugas-table tbody td { padding: 15px 18px; font-size: 13.5px; color: var(--ink); vertical-align: middle; }
|
||||
.tugas-table tbody td.center { text-align: center; }
|
||||
|
||||
/* JUDUL CELL */
|
||||
.judul-cell { font-weight: 700; color: var(--ink); line-height: 1.3; }
|
||||
.mapel-pill {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--blue-soft); border: 1px solid var(--blue-border);
|
||||
border-radius: 999px; padding: 3px 11px;
|
||||
font-size: 12px; font-weight: 600; color: var(--blue);
|
||||
}
|
||||
|
||||
/* DEADLINE */
|
||||
.deadline-chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--amber-soft); border: 1px solid var(--amber-border);
|
||||
border-radius: var(--r-md); padding: 5px 12px;
|
||||
font-size: 12px; font-weight: 600; color: var(--amber);
|
||||
font-family: 'JetBrains Mono', monospace; white-space: nowrap;
|
||||
}
|
||||
.deadline-chip svg { width: 12px; height: 12px; }
|
||||
|
||||
/* WAKTU KUMPUL */
|
||||
.kumpul-chip {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--green-soft); border: 1px solid var(--green-border);
|
||||
border-radius: var(--r-md); padding: 5px 12px;
|
||||
font-size: 12px; font-weight: 600; color: var(--green);
|
||||
font-family: 'JetBrains Mono', monospace; white-space: nowrap;
|
||||
}
|
||||
.kumpul-chip svg { width: 12px; height: 12px; }
|
||||
.dash-text { color: var(--text-muted); font-size: 13px; }
|
||||
|
||||
/* NILAI */
|
||||
.nilai-chip {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 48px; padding: 4px 12px; border-radius: var(--r-md);
|
||||
font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700;
|
||||
background: var(--green-soft); color: var(--green); border: 1px solid var(--green-border);
|
||||
}
|
||||
|
||||
/* STATUS BADGES */
|
||||
.badge-status {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
border-radius: 999px; font-size: 12px; font-weight: 700; padding: 4px 12px;
|
||||
}
|
||||
.badge-status::before { content: ''; width: 6px; height: 6px; border-radius: 50%; }
|
||||
.badge-tepat { background: var(--green-soft); color: var(--green); border: 1px solid var(--green-border); }
|
||||
.badge-tepat::before { background: var(--green); }
|
||||
.badge-telat { background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber-border); }
|
||||
.badge-telat::before { background: var(--amber); }
|
||||
.badge-belum { background: var(--red-soft); color: var(--red); border: 1px solid var(--red-border); }
|
||||
.badge-belum::before { background: var(--red); }
|
||||
.badge-default{ background: var(--slate-soft); color: var(--slate); border: 1px solid var(--slate-border); }
|
||||
.badge-default::before{ background: var(--slate); }
|
||||
|
||||
/* ACTION BUTTONS */
|
||||
.action-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||||
.btn-dl {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 6px 13px; font-size: 12.5px; font-weight: 600;
|
||||
font-family: 'Sora', sans-serif;
|
||||
background: var(--green-soft); color: var(--green); border: 1px solid var(--green-border);
|
||||
border-radius: var(--r-md); text-decoration: none; transition: all 0.15s;
|
||||
}
|
||||
.btn-dl:hover { background: #d1fae5; color: var(--green); text-decoration: none; }
|
||||
.btn-dl svg { width: 13px; height: 13px; }
|
||||
|
||||
.btn-kumpul {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 6px 13px; font-size: 12.5px; font-weight: 600;
|
||||
font-family: 'Sora', sans-serif;
|
||||
background: var(--blue); color: #fff; border: none;
|
||||
border-radius: var(--r-md); text-decoration: none; transition: all 0.15s;
|
||||
box-shadow: 0 2px 8px rgba(37,99,235,0.25);
|
||||
}
|
||||
.btn-kumpul:hover { background: var(--blue-dark); color: #fff; text-decoration: none; transform: translateY(-1px); }
|
||||
.btn-kumpul svg { width: 13px; height: 13px; }
|
||||
|
||||
/* EMPTY */
|
||||
.empty-state { padding: 64px 24px; text-align: center; }
|
||||
.empty-icon {
|
||||
width: 60px; height: 60px; background: var(--surface-2); border: 1px solid var(--border);
|
||||
border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 14px;
|
||||
}
|
||||
.empty-icon svg { width: 26px; height: 26px; color: var(--text-muted); }
|
||||
.empty-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
|
||||
.empty-desc { font-size: 13px; color: var(--text-muted); }
|
||||
|
||||
.table-footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 13px 24px; border-top: 1px solid var(--border);
|
||||
background: var(--surface-2); font-size: 12.5px; color: var(--text-muted);
|
||||
flex-wrap: wrap; gap: 6px;
|
||||
}
|
||||
|
||||
/* ── MOBILE CARDS ─────────────────────────────────── */
|
||||
.mobile-list { display: flex; flex-direction: column; gap: 14px; }
|
||||
.m-card {
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl); box-shadow: var(--shadow-sm);
|
||||
padding: 18px; position: relative; overflow: hidden;
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
}
|
||||
.m-card:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
|
||||
.m-card::before {
|
||||
content: ''; position: absolute; left: 0; top: 0; bottom: 0;
|
||||
width: 4px; border-radius: 4px 0 0 4px; background: var(--blue);
|
||||
}
|
||||
.m-card-head {
|
||||
display: flex; align-items: flex-start; justify-content: space-between;
|
||||
gap: 10px; margin-bottom: 10px;
|
||||
}
|
||||
.m-card-judul { font-size: 14px; font-weight: 700; color: var(--ink); line-height: 1.3; }
|
||||
.m-card-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
|
||||
.m-card-foot { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
|
||||
.m-btn-full { width: 100%; justify-content: center; }
|
||||
|
||||
/* ── RESPONSIVE ───────────────────────────────────── */
|
||||
@media (max-width: 1024px) { .stats-row { grid-template-columns: repeat(2,1fr); } }
|
||||
@media (max-width: 768px) {
|
||||
.tugas-page { padding: 16px 16px 48px; }
|
||||
.page-hero { padding: 22px 20px; }
|
||||
.hero-title { font-size: 20px; }
|
||||
.hero-right { display: none; }
|
||||
.stats-row { grid-template-columns: repeat(2,1fr); gap: 10px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="tugas-page">
|
||||
|
||||
{{-- HERO --}}
|
||||
<div class="page-hero">
|
||||
<div class="hero-left">
|
||||
<div class="hero-tag"><span class="hero-tag-dot"></span> Akademik Siswa</div>
|
||||
<h1 class="hero-title">Daftar <em>Tugas</em></h1>
|
||||
<p class="hero-sub">Pantau semua tugas, deadline, dan status pengumpulan kamu</p>
|
||||
</div>
|
||||
<div class="hero-right">
|
||||
@php
|
||||
$total = $tugas->count();
|
||||
$sudah = $tugas->whereNotNull('waktu_kumpul')->count();
|
||||
$belum = $total - $sudah;
|
||||
@endphp
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ $total }}</div>
|
||||
<div class="hero-stat-lbl">Total Tugas</div>
|
||||
</div>
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-val">{{ $belum }}</div>
|
||||
<div class="hero-stat-lbl">Belum Kumpul</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATS --}}
|
||||
@php
|
||||
$sudahKumpul = $tugas->whereNotNull('waktu_kumpul')->count();
|
||||
$tepat = $tugas->where('status','tepat')->count();
|
||||
$telat = $tugas->where('status','telat')->count();
|
||||
$belumKumpul = $tugas->whereNull('waktu_kumpul')->count();
|
||||
@endphp
|
||||
<div class="stats-row">
|
||||
<div class="s-card blue">
|
||||
<div class="s-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="s-label">Total Tugas</div>
|
||||
<div class="s-value">{{ $tugas->count() }}</div>
|
||||
<div class="s-sub">semua tugas</div>
|
||||
</div>
|
||||
<div class="s-card green">
|
||||
<div class="s-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="s-label">Tepat Waktu</div>
|
||||
<div class="s-value">{{ $tepat }}</div>
|
||||
<div class="s-sub">dikumpulkan tepat</div>
|
||||
</div>
|
||||
<div class="s-card amber">
|
||||
<div class="s-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="s-label">Telat</div>
|
||||
<div class="s-value">{{ $telat }}</div>
|
||||
<div class="s-sub">melewati deadline</div>
|
||||
</div>
|
||||
<div class="s-card red">
|
||||
<div class="s-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="s-label">Belum Kumpul</div>
|
||||
<div class="s-value">{{ $belumKumpul }}</div>
|
||||
<div class="s-sub">perlu dikumpulkan</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- DESKTOP TABLE --}}
|
||||
<div class="table-card d-none d-md-block">
|
||||
<div class="table-toolbar">
|
||||
<div class="toolbar-title">
|
||||
<div class="toolbar-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
Daftar Tugas
|
||||
<span class="toolbar-badge">{{ $tugas->count() }} tugas</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="tugas-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Judul Tugas</th>
|
||||
<th>Mata Pelajaran</th>
|
||||
<th>Deadline</th>
|
||||
<th>Waktu Kumpul</th>
|
||||
<th class="center">Nilai</th>
|
||||
<th class="center">Status</th>
|
||||
<th class="center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($tugas as $t)
|
||||
<tr>
|
||||
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
||||
|
||||
<td>
|
||||
<span class="mapel-pill">{{ $t->mapel->nama_mapel ?? '—' }}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<span class="deadline-chip">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
{{ \Carbon\Carbon::parse($t->deadline)->format('d M Y H:i') }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if($t->waktu_kumpul)
|
||||
<span class="kumpul-chip">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
{{ \Carbon\Carbon::parse($t->waktu_kumpul)->format('d M Y H:i') }}
|
||||
</span>
|
||||
@else
|
||||
<span class="dash-text">—</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
@if($t->nilai)
|
||||
<span class="nilai-chip">{{ $t->nilai }}</span>
|
||||
@else
|
||||
<span class="dash-text">—</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
@if($t->status == 'tepat')
|
||||
<span class="badge-status badge-tepat">Tepat</span>
|
||||
@elseif($t->status == 'telat')
|
||||
<span class="badge-status badge-telat">Telat</span>
|
||||
@elseif($t->status == 'lewat')
|
||||
<span class="badge-status badge-belum">Belum</span>
|
||||
@else
|
||||
<span class="badge-status badge-default">Belum</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
<div class="action-group">
|
||||
@if($t->file)
|
||||
<a href="{{ route('guru.tugas.download', $t->id) }}" class="btn-dl">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
|
||||
</svg>
|
||||
Unduh
|
||||
</a>
|
||||
@endif
|
||||
@if(!$t->waktu_kumpul && now()->lt($t->deadline))
|
||||
<a href="{{ route('siswa.tugas.kumpul', $t->id) }}" class="btn-kumpul">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l4-4m0 0l4 4m-4-4v12"/>
|
||||
</svg>
|
||||
Kumpulkan
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="empty-title">Belum Ada Tugas</div>
|
||||
<div class="empty-desc">Tugas yang diberikan guru akan muncul di sini.</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($tugas->count() > 0)
|
||||
<div class="table-footer">
|
||||
<span>Menampilkan {{ $tugas->count() }} tugas</span>
|
||||
<span>{{ now()->format('d F Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,21 +1,392 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
|
||||
<h4>Kumpulkan Tugas</h4>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<form action="{{ route('siswa.tugas.store', $tugas->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--neutral-light: #F9FAFB;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Upload Jawaban</label>
|
||||
<input type="file" name="file" class="form-control" required>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
.sw-page {
|
||||
padding: 28px 32px;
|
||||
background: var(--bg);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── TOPBAR ── */
|
||||
.sw-topbar { margin-bottom: 28px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 4px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
/* ── LAYOUT ── */
|
||||
.sw-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 360px;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* ── CARD ── */
|
||||
.sw-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── CARD HEADER ── */
|
||||
.sw-card-banner {
|
||||
height: 8px;
|
||||
background: linear-gradient(90deg, #4F46E5, #7C3AED, #059669);
|
||||
}
|
||||
.sw-card-hd {
|
||||
padding: 24px 28px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.sw-card-hd-top {
|
||||
display: flex; align-items: flex-start; gap: 14px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.sw-card-hd-icon {
|
||||
width: 48px; height: 48px; border-radius: 12px;
|
||||
background: var(--primary); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 4px 12px rgba(79,70,229,.3);
|
||||
}
|
||||
.sw-card-hd-title {
|
||||
font-size: 19px; font-weight: 800;
|
||||
color: var(--text-dark); margin: 0 0 5px;
|
||||
letter-spacing: -.3px; line-height: 1.3;
|
||||
}
|
||||
.sw-card-hd-mapel {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: var(--primary-light); border: 1px solid #C7D2FE;
|
||||
border-radius: 99px; padding: 4px 12px;
|
||||
font-size: 12px; font-weight: 600; color: var(--primary);
|
||||
}
|
||||
|
||||
/* meta row */
|
||||
.sw-meta-row {
|
||||
display: flex; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
.sw-meta-chip {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
background: var(--neutral-light); border: 1px solid var(--border);
|
||||
border-radius: 8px; padding: 5px 12px;
|
||||
font-size: 12px; font-weight: 600; color: var(--text-mid);
|
||||
}
|
||||
.sw-meta-chip svg { color: var(--text-soft); }
|
||||
|
||||
/* ── CARD BODY ── */
|
||||
.sw-card-bd { padding: 28px; }
|
||||
|
||||
/* ── SECTION ── */
|
||||
.sw-section {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sw-section-label {
|
||||
font-size: 11.5px; font-weight: 700; color: var(--primary);
|
||||
text-transform: uppercase; letter-spacing: .8px; white-space: nowrap;
|
||||
}
|
||||
.sw-section-line {
|
||||
flex: 1; height: 1px;
|
||||
background: linear-gradient(to right, #C7D2FE, transparent);
|
||||
}
|
||||
|
||||
/* ── UPLOAD ZONE ── */
|
||||
.sw-upload-zone {
|
||||
border: 2px dashed #C7D2FE;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 40px 28px;
|
||||
text-align: center;
|
||||
background: var(--primary-light);
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sw-upload-zone:hover, .sw-upload-zone.dragover {
|
||||
border-color: var(--primary);
|
||||
background: #E0E7FF;
|
||||
}
|
||||
.sw-upload-zone input[type="file"] {
|
||||
position: absolute; inset: 0;
|
||||
opacity: 0; cursor: pointer;
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
.sw-upload-icon {
|
||||
width: 64px; height: 64px;
|
||||
background: var(--surface);
|
||||
border: 1.5px solid #C7D2FE;
|
||||
border-radius: 16px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
color: var(--primary);
|
||||
box-shadow: 0 2px 8px rgba(79,70,229,.1);
|
||||
}
|
||||
.sw-upload-title { font-size: 15px; font-weight: 700; color: var(--text-dark); margin-bottom: 6px; }
|
||||
.sw-upload-sub { font-size: 13px; color: var(--text-soft); margin-bottom: 14px; }
|
||||
.sw-upload-formats {
|
||||
display: flex; justify-content: center; flex-wrap: wrap; gap: 6px;
|
||||
}
|
||||
.sw-format-chip {
|
||||
background: var(--surface); border: 1px solid #C7D2FE;
|
||||
border-radius: 6px; padding: 3px 10px;
|
||||
font-size: 11px; font-weight: 700; color: var(--primary);
|
||||
}
|
||||
|
||||
/* ── FILE SELECTED STATE ── */
|
||||
.sw-file-selected {
|
||||
display: none;
|
||||
align-items: center; gap: 14px;
|
||||
background: var(--success-light);
|
||||
border: 1.5px solid #A7F3D0;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sw-file-selected.show { display: flex; }
|
||||
.sw-file-icon {
|
||||
width: 42px; height: 42px; border-radius: 10px;
|
||||
background: var(--success); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sw-file-name { font-size: 13.5px; font-weight: 700; color: var(--success); word-break: break-all; }
|
||||
.sw-file-size { font-size: 12px; color: #065F46; margin-top: 2px; }
|
||||
|
||||
/* ── SUBMIT BUTTON ── */
|
||||
.sw-btn-submit {
|
||||
width: 100%; padding: 15px;
|
||||
border-radius: var(--radius-md); border: none;
|
||||
background: linear-gradient(135deg, var(--primary), #7C3AED);
|
||||
color: #fff; font-size: 15px; font-weight: 700;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
cursor: pointer; transition: all .2s;
|
||||
box-shadow: 0 4px 16px rgba(79,70,229,.35);
|
||||
display: flex; align-items: center; justify-content: center; gap: 9px;
|
||||
}
|
||||
.sw-btn-submit:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(79,70,229,.4); }
|
||||
|
||||
/* ── SIDEBAR CARD ── */
|
||||
.sw-sidebar { display: flex; flex-direction: column; gap: 16px; }
|
||||
|
||||
.sw-info-card { padding: 20px; }
|
||||
.sw-info-card-title {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 13px; font-weight: 700; color: var(--text-dark);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.sw-info-card-title svg { color: var(--primary); }
|
||||
|
||||
.sw-tip-list { display: flex; flex-direction: column; gap: 10px; }
|
||||
.sw-tip {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
font-size: 12.5px; color: var(--text-mid); line-height: 1.5;
|
||||
}
|
||||
.sw-tip-dot {
|
||||
width: 20px; height: 20px; border-radius: 50%;
|
||||
background: var(--primary-light);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0; margin-top: 1px;
|
||||
font-size: 10px; font-weight: 800; color: var(--primary);
|
||||
}
|
||||
|
||||
/* warning box */
|
||||
.sw-warn-box {
|
||||
background: var(--warning-light);
|
||||
border: 1px solid #FDE68A;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 14px 16px;
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
font-size: 12.5px; color: var(--warning); line-height: 1.6;
|
||||
}
|
||||
.sw-warn-box svg { flex-shrink: 0; margin-top: 1px; }
|
||||
|
||||
/* ── RESPONSIVE ── */
|
||||
@media (max-width: 900px) {
|
||||
.sw-layout { grid-template-columns: 1fr; }
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-card-bd { padding: 20px; }
|
||||
.sw-card-hd { padding: 18px 20px 16px; }
|
||||
.sw-upload-zone { padding: 28px 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<h3>Kumpulkan Tugas</h3>
|
||||
<p>Upload jawaban tugas Anda sebelum batas waktu</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-layout">
|
||||
|
||||
{{-- ── MAIN CARD ── --}}
|
||||
<div class="sw-card">
|
||||
<div class="sw-card-banner"></div>
|
||||
|
||||
<div class="sw-card-hd">
|
||||
<div class="sw-card-hd-top">
|
||||
<div class="sw-card-hd-icon">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-card-hd-title">{{ $tugas->judul }}</div>
|
||||
<div class="sw-card-hd-mapel">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
||||
{{ $tugas->mapel->nama_mapel ?? '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-meta-row">
|
||||
<div class="sw-meta-chip">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
Tugas
|
||||
</div>
|
||||
<div class="sw-meta-chip">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
Upload File
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-card-bd">
|
||||
<form action="{{ route('siswa.tugas.store', $tugas->id) }}" method="POST" enctype="multipart/form-data" id="sw-form">
|
||||
@csrf
|
||||
|
||||
<div class="sw-section">
|
||||
<span class="sw-section-label">Upload Jawaban</span>
|
||||
<div class="sw-section-line"></div>
|
||||
</div>
|
||||
|
||||
{{-- File selected preview --}}
|
||||
<div class="sw-file-selected" id="sw-file-preview">
|
||||
<div class="sw-file-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-file-name" id="sw-file-name">-</div>
|
||||
<div class="sw-file-size" id="sw-file-size">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Upload Zone --}}
|
||||
<div class="sw-upload-zone" id="sw-upload-zone">
|
||||
<input type="file" name="file" required id="sw-file-input">
|
||||
<div class="sw-upload-icon">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
</div>
|
||||
<div class="sw-upload-title">Klik untuk pilih file</div>
|
||||
<div class="sw-upload-sub">atau seret & lepas file jawaban ke sini</div>
|
||||
<div class="sw-upload-formats">
|
||||
<span class="sw-format-chip">PDF</span>
|
||||
<span class="sw-format-chip">DOC</span>
|
||||
<span class="sw-format-chip">DOCX</span>
|
||||
<span class="sw-format-chip">JPG</span>
|
||||
<span class="sw-format-chip">PNG</span>
|
||||
<span class="sw-format-chip">ZIP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="sw-btn-submit">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
Upload Tugas Sekarang
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Upload</button>
|
||||
{{-- ── SIDEBAR ── --}}
|
||||
<div class="sw-sidebar">
|
||||
|
||||
</form>
|
||||
{{-- Tips --}}
|
||||
<div class="sw-card sw-info-card">
|
||||
<div class="sw-info-card-title">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||||
Panduan Upload
|
||||
</div>
|
||||
<div class="sw-tip-list">
|
||||
<div class="sw-tip">
|
||||
<div class="sw-tip-dot">1</div>
|
||||
Pastikan file jawaban sudah lengkap sebelum diupload
|
||||
</div>
|
||||
<div class="sw-tip">
|
||||
<div class="sw-tip-dot">2</div>
|
||||
Gunakan format PDF untuk hasil terbaik
|
||||
</div>
|
||||
<div class="sw-tip">
|
||||
<div class="sw-tip-dot">3</div>
|
||||
Beri nama file dengan nama lengkap Anda
|
||||
</div>
|
||||
<div class="sw-tip">
|
||||
<div class="sw-tip-dot">4</div>
|
||||
Pastikan koneksi internet stabil saat upload
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Warning --}}
|
||||
<div class="sw-warn-box">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||
<div>
|
||||
<strong>Perhatian!</strong><br>
|
||||
Tugas yang sudah dikumpulkan tidak dapat diubah. Pastikan file sudah benar sebelum submit.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const fileInput = document.getElementById('sw-file-input');
|
||||
const preview = document.getElementById('sw-file-preview');
|
||||
const nameEl = document.getElementById('sw-file-name');
|
||||
const sizeEl = document.getElementById('sw-file-size');
|
||||
const zone = document.getElementById('sw-upload-zone');
|
||||
|
||||
fileInput.addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
nameEl.textContent = file.name;
|
||||
sizeEl.textContent = (file.size / 1024 / 1024).toFixed(2) + ' MB';
|
||||
preview.classList.add('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Drag & drop visual
|
||||
zone.addEventListener('dragover', e => { e.preventDefault(); zone.classList.add('dragover'); });
|
||||
zone.addEventListener('dragleave', () => zone.classList.remove('dragover'));
|
||||
zone.addEventListener('drop', e => { e.preventDefault(); zone.classList.remove('dragover'); });
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
@ -2,15 +2,254 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<h2>Hasil Ujian</h2>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<div class="card p-4">
|
||||
<h3>Nilai: {{ $nilai }}</h3>
|
||||
<p>Benar: {{ $benar }} / {{ $total }}</p>
|
||||
:root {
|
||||
--navy: #0F1A2E;
|
||||
--navy-light: #1E3050;
|
||||
--accent: #3B6FE8;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--warning: #D97706;
|
||||
--warning-light: #FFFBEB;
|
||||
--danger: #DC2626;
|
||||
--danger-light: #FEF2F2;
|
||||
--bg: #F0F4FA;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-soft: #6B7280;
|
||||
}
|
||||
|
||||
<a href="{{ route('siswa.dashboard') }}" class="btn btn-primary">
|
||||
Kembali ke Dashboard
|
||||
</a>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
#content { padding: 0 !important; background: var(--bg) !important; }
|
||||
#content-wrapper { background: var(--bg) !important; }
|
||||
|
||||
/* ── PAGE ── */
|
||||
.hasil-wrapper {
|
||||
min-height: calc(100vh - 70px);
|
||||
background: var(--bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
/* ── CARD ── */
|
||||
.result-card {
|
||||
background: var(--surface);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 8px 40px rgba(15,26,46,.12);
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── CARD TOP (navy) ── */
|
||||
.card-top {
|
||||
background: linear-gradient(135deg, var(--navy) 0%, var(--navy-light) 100%);
|
||||
padding: 32px 32px 28px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-top::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 200px; height: 200px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.05);
|
||||
top: -60px; right: -60px;
|
||||
}
|
||||
.card-top::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 120px; height: 120px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.04);
|
||||
bottom: -40px; left: -30px;
|
||||
}
|
||||
|
||||
/* ── SCORE RING ── */
|
||||
.score-ring {
|
||||
position: relative; z-index: 2;
|
||||
width: 110px; height: 110px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.1);
|
||||
border: 3px solid rgba(255,255,255,.2);
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
}
|
||||
.score-num {
|
||||
font-size: 36px; font-weight: 800;
|
||||
color: #fff; line-height: 1;
|
||||
}
|
||||
.score-sub {
|
||||
font-size: 11px; font-weight: 600;
|
||||
color: rgba(255,255,255,.5);
|
||||
text-transform: uppercase; letter-spacing: .4px;
|
||||
}
|
||||
.card-top h3 {
|
||||
position: relative; z-index: 2;
|
||||
font-size: 16px; font-weight: 700;
|
||||
color: rgba(255,255,255,.85); margin: 0;
|
||||
}
|
||||
|
||||
/* ── CARD BODY ── */
|
||||
.card-body { padding: 28px 32px; }
|
||||
|
||||
/* ── STAT ROW ── */
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px; margin-bottom: 22px;
|
||||
}
|
||||
.stat-item {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 14px 16px; text-align: center;
|
||||
}
|
||||
.stat-val {
|
||||
font-size: 22px; font-weight: 800;
|
||||
color: var(--text-dark); margin-bottom: 2px;
|
||||
}
|
||||
.stat-lbl {
|
||||
font-size: 11.5px; font-weight: 600;
|
||||
color: var(--text-soft);
|
||||
text-transform: uppercase; letter-spacing: .3px;
|
||||
}
|
||||
|
||||
/* ── PROGRESS ── */
|
||||
.progress-wrap { margin-bottom: 20px; }
|
||||
.progress-head {
|
||||
display: flex; justify-content: space-between;
|
||||
align-items: center; margin-bottom: 8px;
|
||||
}
|
||||
.progress-title { font-size: 12.5px; font-weight: 600; color: var(--text-soft); }
|
||||
.progress-pct { font-size: 12.5px; font-weight: 700; color: var(--accent); }
|
||||
.progress-track {
|
||||
height: 10px; background: #F1F5F9;
|
||||
border-radius: 99px; overflow: hidden;
|
||||
}
|
||||
.progress-fill {
|
||||
height: 100%; border-radius: 99px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--success));
|
||||
}
|
||||
|
||||
/* ── FEEDBACK ── */
|
||||
.feedback-badge {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
border-radius: 12px; padding: 13px 16px;
|
||||
margin-bottom: 22px;
|
||||
font-size: 13.5px; font-weight: 700;
|
||||
}
|
||||
.fb-success { background: var(--success-light); color: var(--success); border: 1px solid #A7F3D0; }
|
||||
.fb-warning { background: var(--warning-light); color: var(--warning); border: 1px solid #FDE68A; }
|
||||
.fb-danger { background: var(--danger-light); color: var(--danger); border: 1px solid #FECACA; }
|
||||
|
||||
/* ── BUTTON ── */
|
||||
.btn-home {
|
||||
display: flex; align-items: center;
|
||||
justify-content: center; gap: 9px;
|
||||
width: 100%; background: var(--navy); color: #fff;
|
||||
border: none; border-radius: 13px; padding: 14px;
|
||||
font-size: 14.5px; font-weight: 800;
|
||||
text-decoration: none; transition: all .18s;
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
box-shadow: 0 4px 16px rgba(15,26,46,.2);
|
||||
}
|
||||
.btn-home:hover {
|
||||
background: var(--navy-light); color: #fff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(15,26,46,.3);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.card-top { padding: 24px 20px 20px; }
|
||||
.card-body { padding: 22px 20px; }
|
||||
.score-num { font-size: 30px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hasil-wrapper">
|
||||
<div class="result-card">
|
||||
|
||||
{{-- TOP (navy) --}}
|
||||
<div class="card-top">
|
||||
<div class="score-ring">
|
||||
<div class="score-num">{{ $nilai }}</div>
|
||||
<div class="score-sub">Nilai</div>
|
||||
</div>
|
||||
<h3>Hasil Ujian — {{ $ujian->judul ?? 'Ujian' }}</h3>
|
||||
</div>
|
||||
|
||||
{{-- BODY --}}
|
||||
<div class="card-body">
|
||||
|
||||
{{-- STAT --}}
|
||||
<div class="stat-row">
|
||||
<div class="stat-item">
|
||||
<div class="stat-val" style="color:var(--success);">{{ $benar }}</div>
|
||||
<div class="stat-lbl">Benar</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-val" style="color:var(--danger);">{{ $total - $benar }}</div>
|
||||
<div class="stat-lbl">Salah</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- PROGRESS --}}
|
||||
<div class="progress-wrap">
|
||||
<div class="progress-head">
|
||||
<span class="progress-title">Jawaban benar {{ $benar }} dari {{ $total }} soal</span>
|
||||
<span class="progress-pct">{{ round(($benar / $total) * 100) }}%</span>
|
||||
</div>
|
||||
<div class="progress-track">
|
||||
<div class="progress-fill"
|
||||
style="width: {{ ($benar / $total) * 100 }}%">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FEEDBACK --}}
|
||||
@if($nilai >= 80)
|
||||
<div class="feedback-badge fb-success">
|
||||
<svg width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Sangat Baik! Pertahankan prestasi ini.
|
||||
</div>
|
||||
@elseif($nilai >= 60)
|
||||
<div class="feedback-badge fb-warning">
|
||||
<svg width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14 10h.01M12 14h.01M10 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Cukup Baik. Masih bisa ditingkatkan!
|
||||
</div>
|
||||
@else
|
||||
<div class="feedback-badge fb-danger">
|
||||
<svg width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/>
|
||||
</svg>
|
||||
Perlu Belajar Lagi. Jangan menyerah!
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- BUTTON --}}
|
||||
<a href="{{ route('siswa.dashboard') }}" class="btn-home">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
|
||||
</svg>
|
||||
Kembali ke Dashboard
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
|
@ -2,17 +2,219 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<h2>Ujian Tersedia</h2>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
@foreach($ujians as $u)
|
||||
<div class="card mb-2 p-3">
|
||||
<h5>{{ $u->judul }}</h5>
|
||||
<p>Durasi: {{ $u->durasi }} menit</p>
|
||||
:root {
|
||||
--primary: #4F46E5;
|
||||
--primary-dark: #3730A3;
|
||||
--primary-light: #EEF2FF;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--info: #0284C7;
|
||||
--info-light: #F0F9FF;
|
||||
--bg: #F3F4F8;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 16px;
|
||||
}
|
||||
|
||||
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="btn btn-primary">
|
||||
Kerjakan
|
||||
</a>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
#content { padding: 0 !important; background: var(--bg) !important; }
|
||||
#content-wrapper { background: var(--bg) !important; }
|
||||
.sw-page { padding: 28px 32px; background: var(--bg); min-height: calc(100vh - 70px); }
|
||||
|
||||
.sw-topbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 28px; flex-wrap: wrap; gap: 12px; }
|
||||
.sw-topbar h3 { font-size: 21px; font-weight: 800; color: var(--text-dark); margin: 0 0 3px; letter-spacing: -.3px; }
|
||||
.sw-topbar p { font-size: 13px; color: var(--text-soft); margin: 0; }
|
||||
.sw-badge-ready { display: inline-flex; align-items: center; gap: 7px; background: var(--primary-light); border: 1px solid #C7D2FE; border-radius: 99px; padding: 7px 16px; font-size: 13px; font-weight: 600; color: var(--primary); }
|
||||
|
||||
.sw-alert-warning { display: flex; align-items: center; gap: 10px; background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 10px; padding: 12px 16px; margin-bottom: 20px; font-size: 13px; font-weight: 600; color: #92400E; }
|
||||
|
||||
.sw-hero-card { position: relative; border-radius: 22px; overflow: hidden; margin-bottom: 24px; box-shadow: 0 12px 40px rgba(79,70,229,.22); }
|
||||
.sw-hero-bg { position: absolute; inset: 0; background: linear-gradient(135deg, #4F46E5 0%, #0284C7 100%); }
|
||||
.sw-hero-blob1 { position: absolute; width: 260px; height: 260px; border-radius: 50%; background: rgba(255,255,255,.07); top: -80px; right: -60px; }
|
||||
.sw-hero-blob2 { position: absolute; width: 160px; height: 160px; border-radius: 50%; background: rgba(255,255,255,.05); bottom: -50px; left: 40px; }
|
||||
.sw-hero-inner { position: relative; z-index: 2; padding: 36px 40px; display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
|
||||
.sw-hero-eyebrow { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.18); border-radius: 99px; padding: 4px 12px; font-size: 11.5px; font-weight: 700; color: rgba(255,255,255,.9); letter-spacing: .4px; text-transform: uppercase; margin-bottom: 12px; }
|
||||
.sw-hero-title { font-size: 24px; font-weight: 800; color: #fff; margin-bottom: 14px; letter-spacing: -.4px; line-height: 1.3; }
|
||||
.sw-hero-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 22px; }
|
||||
.sw-hero-chip { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.2); border-radius: 8px; padding: 5px 12px; font-size: 12px; font-weight: 600; color: #fff; }
|
||||
.sw-btn-start { display: inline-flex; align-items: center; gap: 8px; background: #fff; color: var(--primary); border: none; border-radius: 12px; padding: 12px 26px; font-size: 14px; font-weight: 800; cursor: pointer; text-decoration: none; transition: all .18s; font-family: 'Plus Jakarta Sans', sans-serif; box-shadow: 0 4px 16px rgba(0,0,0,.12); }
|
||||
.sw-btn-start:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,.18); color: var(--primary-dark); text-decoration: none; }
|
||||
.sw-btn-done { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,.2); color: rgba(255,255,255,.75); border: 1px solid rgba(255,255,255,.3); border-radius: 12px; padding: 12px 26px; font-size: 14px; font-weight: 700; cursor: not-allowed; }
|
||||
.sw-hero-deco { width: 100px; height: 100px; border-radius: 24px; background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2); display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: #fff; }
|
||||
|
||||
.sw-stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px; }
|
||||
.sw-stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 20px 22px; display: flex; align-items: center; gap: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.04); }
|
||||
.sw-stat-icon { width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.si-indigo { background: var(--primary-light); color: var(--primary); }
|
||||
.si-blue { background: var(--info-light); color: var(--info); }
|
||||
.si-green { background: var(--success-light); color: var(--success); }
|
||||
.sw-stat-label { font-size: 12px; font-weight: 600; color: var(--text-soft); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 3px; }
|
||||
.sw-stat-value { font-size: 22px; font-weight: 800; color: var(--text-dark); line-height: 1; }
|
||||
|
||||
.sw-empty { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; padding: 64px 24px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,.04); margin-bottom: 24px; }
|
||||
.sw-empty-ring { width: 80px; height: 80px; border-radius: 50%; background: var(--primary-light); border: 2px solid #C7D2FE; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; color: var(--primary); }
|
||||
.sw-empty h5 { font-size: 17px; font-weight: 700; color: var(--text-dark); margin-bottom: 8px; }
|
||||
.sw-empty p { font-size: 13.5px; color: var(--text-soft); margin: 0; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sw-page { padding: 16px; }
|
||||
.sw-hero-inner { padding: 24px; }
|
||||
.sw-hero-deco { display: none; }
|
||||
.sw-hero-title { font-size: 19px; }
|
||||
.sw-stats-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- TOPBAR --}}
|
||||
<div class="sw-topbar">
|
||||
<div>
|
||||
<h3>Ujian Tersedia</h3>
|
||||
<p>Daftar ujian yang dapat Anda kerjakan sekarang</p>
|
||||
</div>
|
||||
<div class="sw-badge-ready">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Siap mengerjakan ujian hari ini?
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- ALERT WARNING --}}
|
||||
@if(session('warning'))
|
||||
<div class="sw-alert-warning">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/>
|
||||
</svg>
|
||||
{{ session('warning') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- DAFTAR UJIAN --}}
|
||||
@forelse($ujians as $u)
|
||||
@php $dikerjakan = in_array($u->id, $sudahDikerjakan); @endphp
|
||||
|
||||
<div class="sw-hero-card" style="{{ $dikerjakan ? 'opacity:0.85;box-shadow:0 8px 24px rgba(0,0,0,.1)' : '' }}">
|
||||
<div class="sw-hero-bg" style="{{ $dikerjakan ? 'background:linear-gradient(135deg,#374151 0%,#6B7280 100%)' : '' }}"></div>
|
||||
<div class="sw-hero-blob1"></div>
|
||||
<div class="sw-hero-blob2"></div>
|
||||
<div class="sw-hero-inner">
|
||||
<div>
|
||||
{{-- EYEBROW --}}
|
||||
<div class="sw-hero-eyebrow">
|
||||
@if($dikerjakan)
|
||||
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
Sudah Dikerjakan
|
||||
@else
|
||||
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
Ujian Aktif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- JUDUL --}}
|
||||
<div class="sw-hero-title">{{ $u->judul }}</div>
|
||||
|
||||
{{-- CHIPS --}}
|
||||
<div class="sw-hero-chips">
|
||||
<span class="sw-hero-chip">
|
||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
{{ $u->durasi }} menit
|
||||
</span>
|
||||
{{-- Ganti chip jenis yang ada di sw-hero-chips --}}
|
||||
@if($u->jenis)
|
||||
<span class="sw-hero-chip" style="{{ $u->jenis == 'UTS' ? 'background:rgba(2,132,199,.25);border-color:rgba(2,132,199,.4);' : 'background:rgba(217,119,6,.25);border-color:rgba(217,119,6,.4);' }} font-weight:800;">
|
||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
{{ $u->jenis }} — {{ $u->jenis == 'UTS' ? 'Ujian Tengah Semester' : 'Ujian Akhir Semester' }}
|
||||
</span>
|
||||
@endif
|
||||
@if($u->mulai)
|
||||
<span class="sw-hero-chip">
|
||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
{{ $u->mulai }}
|
||||
</span>
|
||||
@endif
|
||||
{{-- CHIP NILAI (muncul kalau sudah dikerjakan) --}}
|
||||
@if($dikerjakan && isset($hasilSiswa[$u->id]))
|
||||
<span class="sw-hero-chip" style="background:rgba(255,255,255,.3);font-weight:800;font-size:13px;">
|
||||
<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10"/></svg>
|
||||
Nilai: {{ $hasilSiswa[$u->id]->nilai }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- TOMBOL --}}
|
||||
@if($dikerjakan)
|
||||
<span class="sw-btn-done">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
Sudah Selesai Dikerjakan
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ route('siswa.ujian.kerjakan', $u->id) }}" class="sw-btn-start">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
Mulai Ujian Sekarang
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- DECO ICON --}}
|
||||
<div class="sw-hero-deco">
|
||||
@if($dikerjakan)
|
||||
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
@else
|
||||
<svg width="44" height="44" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@empty
|
||||
<div class="sw-empty">
|
||||
<div class="sw-empty-ring">
|
||||
<svg width="32" height="32" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
||||
</div>
|
||||
<h5>Tidak Ada Ujian</h5>
|
||||
<p>Saat ini belum ada ujian yang tersedia. Silakan cek kembali nanti.</p>
|
||||
</div>
|
||||
@endforelse
|
||||
|
||||
{{-- MINI INFO --}}
|
||||
<div class="sw-stats-row">
|
||||
<div class="sw-stat-card">
|
||||
<div class="sw-stat-icon si-indigo">
|
||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-label">Total Ujian</div>
|
||||
<div class="sw-stat-value">{{ $ujians->count() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-stat-card">
|
||||
<div class="sw-stat-icon si-blue">
|
||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-label">Sudah Dikerjakan</div>
|
||||
<div class="sw-stat-value" style="color:var(--info);">{{ count($sudahDikerjakan) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-stat-card">
|
||||
<div class="sw-stat-icon si-green">
|
||||
<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sw-stat-label">Mode</div>
|
||||
<div class="sw-stat-value" style="font-size:16px;color:var(--success);">Online</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -2,114 +2,337 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<div class="container-fluid">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
<h3 class="mb-3">{{ $ujian->judul }}</h3>
|
||||
:root {
|
||||
--navy: #0F1A2E;
|
||||
--navy-mid: #162236;
|
||||
--navy-light: #1E3050;
|
||||
--accent: #3B6FE8;
|
||||
--success: #059669;
|
||||
--success-light: #ECFDF5;
|
||||
--bg: #F0F4FA;
|
||||
--surface: #FFFFFF;
|
||||
--border: #E5E7EB;
|
||||
--text-dark: #111827;
|
||||
--text-mid: #374151;
|
||||
--text-soft: #6B7280;
|
||||
}
|
||||
|
||||
{{-- TIMER --}}
|
||||
<div class="alert alert-info">
|
||||
⏱ Sisa waktu: <b id="timer"></b>
|
||||
* { font-family: 'Plus Jakarta Sans', sans-serif; box-sizing: border-box; }
|
||||
|
||||
#content { padding: 0 !important; background: var(--bg) !important; }
|
||||
#content-wrapper { background: var(--bg) !important; }
|
||||
|
||||
.sw-page { padding: 22px 28px; background: var(--bg); min-height: calc(100vh - 70px); }
|
||||
|
||||
/* ── HEADER ── */
|
||||
.ujian-header {
|
||||
display: flex; align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px; flex-wrap: wrap; margin-bottom: 22px;
|
||||
}
|
||||
.ujian-eyebrow {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
background: rgba(59,111,232,.1); border: 1px solid rgba(59,111,232,.2);
|
||||
border-radius: 99px; padding: 4px 12px;
|
||||
font-size: 11.5px; font-weight: 700; color: var(--accent);
|
||||
letter-spacing: .4px; text-transform: uppercase; margin-bottom: 8px;
|
||||
}
|
||||
.ujian-title {
|
||||
font-size: 20px; font-weight: 800;
|
||||
color: var(--text-dark); margin: 0; letter-spacing: -.3px;
|
||||
}
|
||||
|
||||
/* ── TIMER ── */
|
||||
.timer-wrap {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
background: var(--navy); border-radius: 14px;
|
||||
padding: 12px 20px; box-shadow: 0 4px 16px rgba(15,26,46,.25);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.timer-label {
|
||||
font-size: 11px; font-weight: 600;
|
||||
color: rgba(255,255,255,.5); text-transform: uppercase;
|
||||
letter-spacing: .5px; margin-bottom: 2px;
|
||||
}
|
||||
.timer-value {
|
||||
font-size: 22px; font-weight: 800;
|
||||
color: #fff; letter-spacing: 2px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
#timer-dot {
|
||||
width: 8px; height: 8px; border-radius: 50%;
|
||||
background: #22C55E; flex-shrink: 0;
|
||||
animation: timerPulse 1.2s ease infinite;
|
||||
}
|
||||
@keyframes timerPulse {
|
||||
0%,100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: .4; transform: scale(.6); }
|
||||
}
|
||||
|
||||
/* ── LAYOUT ── */
|
||||
.ujian-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 260px;
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* ── SOAL CARD ── */
|
||||
.soal-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,.04);
|
||||
}
|
||||
.soal-head {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 14px 20px;
|
||||
background: linear-gradient(135deg, var(--navy) 0%, var(--navy-light) 100%);
|
||||
border-bottom: 1px solid rgba(255,255,255,.06);
|
||||
}
|
||||
.soal-num {
|
||||
width: 30px; height: 30px; border-radius: 8px;
|
||||
background: var(--accent);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 13px; font-weight: 800; color: #fff; flex-shrink: 0;
|
||||
}
|
||||
.soal-head-label {
|
||||
font-size: 13px; font-weight: 600;
|
||||
color: rgba(255,255,255,.75);
|
||||
}
|
||||
.soal-body { padding: 22px 20px; }
|
||||
.pertanyaan {
|
||||
font-size: 14.5px; font-weight: 600;
|
||||
color: var(--text-dark); line-height: 1.75; margin-bottom: 18px;
|
||||
}
|
||||
|
||||
/* ── OPSI ── */
|
||||
.opsi-item { display: block; cursor: pointer; margin-bottom: 10px; }
|
||||
.opsi-item input { display: none; }
|
||||
.opsi-inner {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
background: #F8FAFC; border: 1.5px solid var(--border);
|
||||
border-radius: 11px; padding: 11px 15px; transition: all .15s;
|
||||
}
|
||||
.opsi-item:hover .opsi-inner {
|
||||
border-color: var(--accent); background: #EEF4FF;
|
||||
}
|
||||
.opsi-item input:checked + .opsi-inner {
|
||||
background: rgba(59,111,232,.07);
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(59,111,232,.1);
|
||||
}
|
||||
.opsi-key {
|
||||
width: 28px; height: 28px; border-radius: 7px;
|
||||
background: var(--border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; font-weight: 800; color: var(--text-soft);
|
||||
flex-shrink: 0; transition: all .15s;
|
||||
}
|
||||
.opsi-item input:checked + .opsi-inner .opsi-key {
|
||||
background: var(--accent); color: #fff;
|
||||
}
|
||||
.opsi-text {
|
||||
font-size: 13.5px; font-weight: 500; color: var(--text-mid);
|
||||
}
|
||||
.opsi-item input:checked + .opsi-inner .opsi-text {
|
||||
color: var(--accent); font-weight: 600;
|
||||
}
|
||||
|
||||
/* ── SUBMIT ── */
|
||||
.submit-wrap {
|
||||
display: flex; justify-content: flex-end; padding-bottom: 32px;
|
||||
}
|
||||
.btn-finish {
|
||||
display: inline-flex; align-items: center; gap: 9px;
|
||||
background: var(--navy); color: #fff; border: none;
|
||||
border-radius: 13px; padding: 13px 30px;
|
||||
font-size: 14.5px; font-weight: 800; cursor: pointer;
|
||||
transition: all .18s; font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
box-shadow: 0 4px 16px rgba(15,26,46,.25);
|
||||
}
|
||||
.btn-finish:hover {
|
||||
background: var(--navy-light);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(15,26,46,.35);
|
||||
}
|
||||
|
||||
/* ── NAVIGASI SOAL ── */
|
||||
.nav-card {
|
||||
background: var(--navy);
|
||||
border-radius: 18px; padding: 18px;
|
||||
box-shadow: 0 4px 20px rgba(15,26,46,.25);
|
||||
position: sticky; top: 90px;
|
||||
}
|
||||
.nav-title {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 11.5px; font-weight: 700;
|
||||
color: rgba(255,255,255,.55);
|
||||
text-transform: uppercase; letter-spacing: .5px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.nav-grid { display: flex; flex-wrap: wrap; gap: 7px; }
|
||||
.nav-number {
|
||||
width: 38px; height: 38px; border-radius: 9px;
|
||||
border: 1.5px solid rgba(255,255,255,.12);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
text-decoration: none; color: rgba(255,255,255,.7);
|
||||
font-size: 12.5px; font-weight: 700; transition: all .15s;
|
||||
background: rgba(255,255,255,.05);
|
||||
}
|
||||
.nav-number:hover {
|
||||
background: var(--accent); border-color: var(--accent); color: #fff;
|
||||
}
|
||||
.nav-number.answered {
|
||||
background: var(--success); border-color: var(--success); color: #fff;
|
||||
}
|
||||
.nav-legend {
|
||||
display: flex; gap: 12px; margin-top: 14px; flex-wrap: wrap;
|
||||
}
|
||||
.nav-leg-item {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-size: 11px; font-weight: 600; color: rgba(255,255,255,.45);
|
||||
}
|
||||
.nav-leg-dot { width: 10px; height: 10px; border-radius: 3px; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.ujian-layout { grid-template-columns: 1fr; }
|
||||
.nav-card { position: relative; top: 0; }
|
||||
.sw-page { padding: 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sw-page">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="ujian-header">
|
||||
<div>
|
||||
<div class="ujian-eyebrow">
|
||||
<svg width="11" height="11" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
</svg>
|
||||
Sedang Berlangsung
|
||||
</div>
|
||||
<h2 class="ujian-title">{{ $ujian->judul }}</h2>
|
||||
</div>
|
||||
<div class="timer-wrap">
|
||||
<div>
|
||||
<div class="timer-label">Sisa Waktu</div>
|
||||
<div class="timer-value"><span id="timer"></span></div>
|
||||
</div>
|
||||
<div id="timer-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('siswa.ujian.submit', $ujian->id) }}">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
|
||||
{{-- ================= KIRI: SOAL ================= --}}
|
||||
<div class="col-md-9">
|
||||
<div class="ujian-layout">
|
||||
|
||||
{{-- SOAL --}}
|
||||
<div>
|
||||
@foreach($soals as $i => $s)
|
||||
<div class="card mb-3 p-3" id="soal{{ $s->id }}">
|
||||
|
||||
<b>Soal {{ $loop->iteration }}</b><br>
|
||||
{{ $s->pertanyaan }}
|
||||
|
||||
<hr>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="jawaban[{{ $s->id }}]" value="a">
|
||||
{{ $s->a }}
|
||||
</label><br>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="jawaban[{{ $s->id }}]" value="b">
|
||||
{{ $s->b }}
|
||||
</label><br>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="jawaban[{{ $s->id }}]" value="c">
|
||||
{{ $s->c }}
|
||||
</label><br>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="jawaban[{{ $s->id }}]" value="d">
|
||||
{{ $s->d }}
|
||||
</label><br>
|
||||
|
||||
<div class="soal-card" id="soal{{ $s->id }}">
|
||||
<div class="soal-head">
|
||||
<div class="soal-num">{{ $loop->iteration }}</div>
|
||||
<div class="soal-head-label">Soal Nomor {{ $loop->iteration }}</div>
|
||||
</div>
|
||||
<div class="soal-body">
|
||||
<p class="pertanyaan">{{ $s->pertanyaan }}</p>
|
||||
<div>
|
||||
@foreach(['a','b','c','d'] as $opt)
|
||||
<label class="opsi-item">
|
||||
<input type="radio"
|
||||
name="jawaban[{{ $s->id }}]"
|
||||
value="{{ $opt }}">
|
||||
<div class="opsi-inner">
|
||||
<div class="opsi-key">{{ strtoupper($opt) }}</div>
|
||||
<div class="opsi-text">{{ $s->$opt }}</div>
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- BUTTON FINISH --}}
|
||||
<div class="text-end mb-5">
|
||||
<button class="btn btn-success btn-lg">
|
||||
✅ Finish Ujian
|
||||
<div class="submit-wrap">
|
||||
<button type="submit" class="btn-finish"
|
||||
onclick="return confirm('Yakin ingin mengumpulkan ujian?')">
|
||||
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Selesai & Kumpulkan Ujian
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ================= KANAN: DAFTAR SOAL ================= --}}
|
||||
<div class="col-md-3">
|
||||
|
||||
<div class="card p-3">
|
||||
|
||||
<b>Daftar Soal</b>
|
||||
|
||||
<div class="d-flex flex-wrap mt-2">
|
||||
@foreach($soals as $i => $s)
|
||||
<a href="#soal{{ $s->id }}"
|
||||
class="btn btn-sm btn-outline-primary m-1">
|
||||
{{ $loop->iteration }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- NAVIGASI SOAL --}}
|
||||
<div class="nav-card">
|
||||
<div class="nav-title">
|
||||
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>
|
||||
Navigasi Soal
|
||||
</div>
|
||||
<div class="nav-grid">
|
||||
@foreach($soals as $s)
|
||||
<a href="#soal{{ $s->id }}"
|
||||
class="nav-number"
|
||||
id="nav{{ $s->id }}">
|
||||
{{ $loop->iteration }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="nav-legend">
|
||||
<div class="nav-leg-item">
|
||||
<div class="nav-leg-dot" style="background:#059669;"></div>
|
||||
Sudah dijawab
|
||||
</div>
|
||||
<div class="nav-leg-item">
|
||||
<div class="nav-leg-dot" style="background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.2);"></div>
|
||||
Belum dijawab
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{-- ================= TIMER SCRIPT ================= --}}
|
||||
<script>
|
||||
|
||||
// TIMER
|
||||
let waktu = {{ $ujian->durasi * 60 }};
|
||||
|
||||
let timer = setInterval(function() {
|
||||
|
||||
let timerInterval = setInterval(function () {
|
||||
let menit = Math.floor(waktu / 60);
|
||||
let detik = waktu % 60;
|
||||
|
||||
let detik = waktu % 60;
|
||||
document.getElementById('timer').innerHTML =
|
||||
menit + " : " + (detik < 10 ? "0" + detik : detik);
|
||||
|
||||
waktu--;
|
||||
|
||||
if (waktu < 0) {
|
||||
clearInterval(timer);
|
||||
clearInterval(timerInterval);
|
||||
alert("Waktu habis!");
|
||||
document.querySelector('form').submit();
|
||||
}
|
||||
|
||||
}, 1000);
|
||||
|
||||
// AUTO HIGHLIGHT NAV
|
||||
document.querySelectorAll('input[type=radio]').forEach(el => {
|
||||
el.addEventListener('change', function () {
|
||||
let soalId = this.name.match(/\[(\d+)\]/)[1];
|
||||
let navEl = document.getElementById('nav' + soalId);
|
||||
navEl.classList.add('answered');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
288
routes/web.php
288
routes/web.php
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use App\Http\Controllers\AuthController;
|
||||
|
||||
|
|
@ -16,13 +17,14 @@
|
|||
|
||||
// ================= GURU =================
|
||||
use App\Http\Controllers\Guru\DashboardController as GuruDashboard;
|
||||
use App\Http\Controllers\Guru\DashboardController;
|
||||
use App\Http\Controllers\Guru\JadwalController as GuruJadwal;
|
||||
use App\Http\Controllers\Guru\AbsensiController;
|
||||
use App\Http\Controllers\Guru\NilaiController as GuruNilai;
|
||||
use App\Http\Controllers\Guru\TugasController;
|
||||
use App\Models\Jadwal;
|
||||
use App\Http\Controllers\Guru\MapelController;
|
||||
|
||||
use App\Http\Controllers\Guru\UjianController as GuruUjian;
|
||||
use App\Models\Jadwal;
|
||||
|
||||
// ================= SISWA =================
|
||||
use App\Http\Controllers\Siswa\DashboardController as SiswaDashboard;
|
||||
|
|
@ -30,58 +32,54 @@
|
|||
use App\Http\Controllers\Siswa\AbsensiController as SiswaAbsensi;
|
||||
use App\Http\Controllers\Siswa\NilaiController as SiswaNilai;
|
||||
use App\Http\Controllers\Siswa\ProfileController as SiswaProfile;
|
||||
use App\Http\Controllers\Siswa\AbsensiController as SiswaAbsensiController;
|
||||
|
||||
//=================otp=================
|
||||
use App\Http\Controllers\Siswa\TugasController as SiswaTugasController;
|
||||
use App\Http\Controllers\Siswa\UjianController as SiswaUjian;
|
||||
|
||||
// ================= AUTH TAMBAHAN =================
|
||||
use App\Http\Controllers\Auth\OtpController;
|
||||
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use App\Http\Controllers\Auth\ForgotPasswordController;
|
||||
use App\Http\Controllers\Auth\ResetPasswordController;
|
||||
|
||||
use App\Http\Controllers\Siswa\UjianController;
|
||||
|
||||
|
||||
use App\Http\Controllers\Guru\UjianController as GuruUjian;
|
||||
use App\Http\Controllers\Siswa\UjianController as SiswaUjian;
|
||||
/*
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| ROOT
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('login');
|
||||
});
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
| LOGIN
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| LOGIN & LOGOUT
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
|
||||
Route::post('/login', [AuthController::class, 'login'])->name('login.process');
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
| LOGOUT
|
||||
|----------------------------------------
|
||||
*/
|
||||
Route::post('/logout', function () {
|
||||
Auth::logout();
|
||||
request()->session()->invalidate();
|
||||
request()->session()->regenerateToken();
|
||||
|
||||
return redirect()->route('login');
|
||||
})->name('logout');
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
| FORCE PASSWORD
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| OTP LOGIN (Langkah 1 & 2 di halaman login)
|
||||
| — sendOtp : kirim OTP ke email SAJA atau WhatsApp SAJA (tidak keduanya)
|
||||
| — loginOtp : login dengan email + password + kode OTP
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
||||
Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| FORCE PASSWORD (ganti password default setelah login pertama)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::middleware('auth')->group(function () {
|
||||
|
||||
Route::get('/force-password', function () {
|
||||
return view('auth.force-password');
|
||||
})->name('force.password');
|
||||
|
|
@ -91,18 +89,43 @@
|
|||
});
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| LUPA PASSWORD — via OTP (flow: kirim OTP → verify → reset)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
// Langkah 1: Form kirim OTP lupa password
|
||||
Route::get('/forgot-password', function () {
|
||||
return view('auth.forgot-password');
|
||||
})->name('password.request');
|
||||
|
||||
Route::post('/forgot-password', [AuthController::class, 'sendOtp'])
|
||||
->name('password.email');
|
||||
|
||||
// Langkah 2: Verify OTP
|
||||
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp'])
|
||||
->name('verify.otp.form');
|
||||
|
||||
Route::post('/verify-otp', [AuthController::class, 'verifyOtp'])
|
||||
->name('verify.otp');
|
||||
|
||||
// Langkah 3: Reset password baru
|
||||
Route::get('/reset-password', [AuthController::class, 'showResetPassword'])
|
||||
->name('reset.password.form');
|
||||
|
||||
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
|
||||
->name('password.update');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ADMIN
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::middleware(['auth', 'force.password'])
|
||||
->prefix('admin')
|
||||
->name('admin.')
|
||||
->group(function () {
|
||||
|
||||
// 🔥 /admin langsung ke dashboard
|
||||
Route::get('/', [AdminDashboard::class, 'index']);
|
||||
|
||||
Route::get('/dashboard', [AdminDashboard::class, 'index'])->name('dashboard');
|
||||
|
||||
Route::resource('guru', GuruController::class);
|
||||
|
|
@ -111,41 +134,27 @@
|
|||
Route::resource('kelas', KelasController::class);
|
||||
Route::resource('jadwal', AdminJadwal::class);
|
||||
|
||||
// 🔥 route tambahan
|
||||
Route::get('jadwal/grid', [AdminJadwal::class, 'grid'])->name('jadwal.grid');
|
||||
|
||||
// PROFILE
|
||||
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
|
||||
Route::post('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
|
||||
// STATUS
|
||||
Route::get('/guru/{id}/nonaktif', [GuruController::class, 'nonaktif'])->name('guru.nonaktif');
|
||||
Route::get('/guru/{id}/aktifkan', [GuruController::class, 'aktifkan'])->name('guru.aktifkan');
|
||||
|
||||
// Route::get('/siswa/{id}/nonaktif', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
|
||||
// Route::post('/nonaktif/{id}', [SiswaController::class, 'nonaktif'])
|
||||
// ->name('admin.siswa.nonaktif');
|
||||
// Route::get('/siswa/{id}/aktifkan', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
|
||||
|
||||
Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif'])->name('siswa.bulkNonaktif');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
|
||||
Route::post('/siswa/nonaktif/{id}', [SiswaController::class, 'nonaktif'])
|
||||
->name('siswa.nonaktif');
|
||||
|
||||
Route::get('/siswa/aktifkan/{id}', [SiswaController::class, 'aktifkan'])
|
||||
->name('siswa.aktifkan');
|
||||
|
||||
|
||||
|
||||
// Siswa nonaktif/aktifkan (di luar middleware force.password agar fleksibel)
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::post('/siswa/nonaktif/{id}', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
|
||||
Route::get('/siswa/aktifkan/{id}', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
|
||||
});
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| GURU
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::middleware(['auth', 'force.password'])
|
||||
->prefix('guru')
|
||||
|
|
@ -157,19 +166,12 @@
|
|||
|
||||
Route::get('/jadwal', [GuruJadwal::class, 'index'])->name('jadwal');
|
||||
|
||||
// =========================
|
||||
// ✅ ABSENSI (FINAL)
|
||||
// =========================
|
||||
Route::get('/absensi', [AbsensiController::class, 'index'])
|
||||
->name('absensi');
|
||||
// ABSENSI
|
||||
Route::get('/absensi', [AbsensiController::class, 'index'])->name('absensi');
|
||||
Route::post('/absensi/simpan', [AbsensiController::class, 'simpan'])->name('absensi.simpan');
|
||||
Route::put('/absensi/update/{id}', [AbsensiController::class, 'update'])->name('absensi.update');
|
||||
|
||||
Route::post('/absensi/simpan', [AbsensiController::class, 'simpan'])
|
||||
->name('absensi.simpan');
|
||||
|
||||
|
||||
// =========================
|
||||
// NILAI
|
||||
// =========================
|
||||
Route::get('/nilai', [GuruNilai::class, 'index'])->name('nilai.index');
|
||||
Route::get('/nilai/input', [GuruNilai::class, 'inputNilai'])->name('nilai.input');
|
||||
Route::get('/get-siswa/{id}', [GuruNilai::class, 'getSiswaByKelas'])->name('get.siswa');
|
||||
|
|
@ -177,91 +179,58 @@
|
|||
Route::get('/nilai/{id}/edit', [GuruNilai::class, 'edit'])->name('nilai.edit');
|
||||
Route::put('/nilai/{id}', [GuruNilai::class, 'update'])->name('nilai.update');
|
||||
|
||||
// =========================
|
||||
// TUGAS
|
||||
// =========================
|
||||
Route::resource('tugas', TugasController::class);
|
||||
Route::get('/tugas/{id}/download', [TugasController::class, 'download'])->name('tugas.download');
|
||||
Route::get('/tugas/{id}/pengumpulan', [TugasController::class, 'pengumpulan'])->name('tugas.pengumpulan');
|
||||
Route::post('/pengumpulan/{id}/nilai', [TugasController::class, 'nilai'])->name('tugas.nilai');
|
||||
|
||||
Route::get('/tugas/{id}/download', [TugasController::class, 'download'])
|
||||
->name('tugas.download');
|
||||
|
||||
Route::get('/tugas/{id}/pengumpulan', [TugasController::class, 'pengumpulan'])
|
||||
->name('tugas.pengumpulan');
|
||||
|
||||
Route::post('/pengumpulan/{id}/nilai', [TugasController::class, 'nilai'])
|
||||
->name('tugas.nilai');
|
||||
|
||||
// =========================
|
||||
// MAPEL
|
||||
// =========================
|
||||
Route::get('/get-mapel-by-kelas/{id}', [MapelController::class, 'getByKelas'])
|
||||
->name('get.mapel');
|
||||
Route::get('/get-mapel-by-kelas/{id}', [MapelController::class, 'getByKelas'])->name('get.mapel');
|
||||
|
||||
// UJIAN
|
||||
Route::get('/ujian', [GuruUjian::class, 'index'])->name('ujian.index');
|
||||
Route::get('/ujian/create', [GuruUjian::class, 'create'])->name('ujian.create');
|
||||
Route::post('/ujian/store', [GuruUjian::class, 'store'])->name('ujian.store');
|
||||
Route::get('/ujian/{id}/edit', [GuruUjian::class, 'edit'])->name('ujian.edit');
|
||||
Route::put('/ujian/{id}', [GuruUjian::class, 'update'])->name('ujian.update');
|
||||
Route::delete('/ujian/{id}', [GuruUjian::class, 'destroy'])->name('ujian.destroy');
|
||||
Route::get('/ujian/{id}/soal', [GuruUjian::class, 'soal'])->name('ujian.soal');
|
||||
Route::post('/ujian/{id}/soal', [GuruUjian::class, 'storeSoal'])->name('ujian.storeSoal');
|
||||
});
|
||||
|
||||
// routes/web.php — tambahkan di dalam group guru
|
||||
|
||||
//==================otp============
|
||||
// Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
||||
// Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
|
||||
// Route::post('/send-otp-login', [OtpController::class, 'sendOtp'])
|
||||
// ->name('send.otp.login');
|
||||
Route::post('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
||||
Route::post('/login-otp', [OtpController::class, 'loginOtp'])->name('login.otp');
|
||||
// di dalam group guru
|
||||
// UJIAN
|
||||
Route::get('/ujian', [GuruUjian::class, 'index'])->name('ujian.index');
|
||||
Route::get('/ujian/create', [GuruUjian::class, 'create'])->name('ujian.create');
|
||||
Route::post('/ujian/store', [GuruUjian::class, 'store'])->name('ujian.store');
|
||||
Route::get('/ujian/{id}/edit', [GuruUjian::class, 'edit'])->name('ujian.edit');
|
||||
Route::put('/ujian/{id}', [GuruUjian::class, 'update'])->name('ujian.update');
|
||||
Route::delete('/ujian/{id}', [GuruUjian::class, 'destroy'])->name('ujian.destroy');
|
||||
Route::get('/ujian/{id}/soal', [GuruUjian::class, 'soal'])->name('ujian.soal');
|
||||
Route::post('/ujian/{id}/soal', [GuruUjian::class, 'storeSoal'])->name('ujian.storeSoal');
|
||||
Route::post('/ujian/{id}/kirim', [GuruUjian::class, 'kirim'])->name('ujian.kirim'); // ✅ TAMBAH DI SINI
|
||||
|
||||
// form email
|
||||
Route::get('/forgot-password', [ForgotPasswordController::class, 'showLinkRequestForm'])
|
||||
->name('password.request');
|
||||
|
||||
// kirim email
|
||||
Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLinkEmail'])
|
||||
->name('password.email');
|
||||
|
||||
// form reset
|
||||
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])
|
||||
->name('password.reset');
|
||||
|
||||
// simpan password baru
|
||||
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])
|
||||
->name('password.update');
|
||||
|
||||
|
||||
Route::get('/forgot-password', function () {
|
||||
return view('auth.forgot-password');
|
||||
})->name('password.request');
|
||||
|
||||
Route::post('/forgot-password', [AuthController::class, 'sendOtp'])
|
||||
->name('password.email');
|
||||
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp'])
|
||||
->name('verify.otp.form');
|
||||
|
||||
Route::post('/verify-otp', [AuthController::class, 'verifyOtp'])
|
||||
->name('verify.otp');
|
||||
|
||||
// FORM RESET PASSWORD (GET)
|
||||
Route::get('/reset-password', [AuthController::class, 'showResetPassword'])
|
||||
->name('reset.password');
|
||||
|
||||
// PROSES RESET PASSWORD (POST)
|
||||
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
|
||||
->name('password.update');
|
||||
|
||||
Route::post('/reset-password', [AuthController::class, 'resetPassword'])
|
||||
->name('password.update');
|
||||
// Guru: cek status middleware tambahan (jika diperlukan)
|
||||
Route::middleware(['auth', 'cek.status'])->prefix('guru')->name('guru.')->group(function () {
|
||||
// tambahkan route khusus cek.status di sini jika ada
|
||||
});
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| SISWA
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
use App\Http\Controllers\Siswa\TugasController as SiswaTugasController;
|
||||
|
||||
Route::middleware(['auth', 'force.password'])
|
||||
->prefix('siswa')
|
||||
->name('siswa.')
|
||||
->group(function () {
|
||||
|
||||
Route::get('/', [SiswaDashboard::class, 'index']);
|
||||
|
||||
Route::get('/dashboard', [SiswaDashboard::class, 'index'])->name('dashboard');
|
||||
|
||||
Route::get('/jadwal', [SiswaJadwal::class, 'index'])->name('jadwal');
|
||||
Route::get('/absensi', [SiswaAbsensi::class, 'index'])->name('absensi');
|
||||
Route::get('/nilai', [SiswaNilai::class, 'index'])->name('nilai');
|
||||
|
|
@ -269,64 +238,29 @@
|
|||
Route::get('/profile', [SiswaProfile::class, 'index'])->name('profile');
|
||||
Route::post('/profile', [SiswaProfile::class, 'update'])->name('profile.update');
|
||||
|
||||
// ✅ SCAN QR
|
||||
Route::get('/absen/{token}', [SiswaAbsensi::class, 'scan'])
|
||||
->name('absensi.scan');
|
||||
// QR Absensi
|
||||
Route::get('/absen/{token}', [SiswaAbsensi::class, 'scan'])->name('absensi.scan');
|
||||
Route::post('/absen/hadir', [SiswaAbsensi::class, 'hadir'])->name('absensi.hadir');
|
||||
|
||||
// ✅ SUBMIT HADIR
|
||||
Route::post('/absen/hadir', [SiswaAbsensi::class, 'hadir'])
|
||||
->name('absensi.hadir');
|
||||
|
||||
// ================= TUGAS =================
|
||||
// TUGAS
|
||||
Route::get('/tugas', [SiswaTugasController::class, 'index'])->name('tugas.index');
|
||||
Route::get('/tugas/{id}/kumpul', [SiswaTugasController::class, 'kumpul'])->name('tugas.kumpul');
|
||||
Route::post('/tugas/{id}/kumpul', [SiswaTugasController::class, 'store'])->name('tugas.store');
|
||||
|
||||
// UJIAN
|
||||
Route::get('/ujian', [SiswaUjian::class, 'index'])->name('ujian.index');
|
||||
Route::get('/ujian/{id}/kerjakan', [SiswaUjian::class, 'kerjakan'])->name('ujian.kerjakan');
|
||||
Route::post('/ujian/{id}/submit', [SiswaUjian::class, 'submit'])->name('ujian.submit');
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
|
||||
Route::prefix('guru')->name('guru.')->group(function () {
|
||||
|
||||
Route::get('/ujian', [\App\Http\Controllers\Guru\UjianController::class, 'index'])->name('ujian.index');
|
||||
|
||||
Route::get('/ujian/create', [\App\Http\Controllers\Guru\UjianController::class, 'create'])->name('ujian.create');
|
||||
|
||||
Route::post('/ujian/store', [\App\Http\Controllers\Guru\UjianController::class, 'store'])->name('ujian.store');
|
||||
|
||||
Route::get('/ujian/{id}/soal', [\App\Http\Controllers\Guru\UjianController::class, 'soal'])->name('ujian.soal');
|
||||
|
||||
Route::post('/ujian/{id}/soal', [\App\Http\Controllers\Guru\UjianController::class, 'storeSoal'])->name('ujian.storeSoal');
|
||||
|
||||
// Route::get('/guru/ujian/{id}/soal', [UjianController::class, 'soal']);
|
||||
// Route::post('/guru/ujian/{id}/soal', [UjianController::class, 'storeSoal']);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->prefix('siswa')->group(function () {
|
||||
|
||||
Route::get('/ujian', [UjianController::class, 'index'])
|
||||
->name('siswa.ujian.index');
|
||||
|
||||
Route::get('/ujian/{id}/kerjakan', [UjianController::class, 'kerjakan'])
|
||||
->name('siswa.ujian.kerjakan');
|
||||
|
||||
Route::post('/ujian/{id}/submit', [UjianController::class, 'submit'])
|
||||
->name('siswa.ujian.submit');
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
|----------------------------------------
|
||||
| TEST EMAIL
|
||||
|----------------------------------------
|
||||
|--------------------------------------------------------------------------
|
||||
| TEST EMAIL (hapus di production)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
Route::get('/test-email', function () {
|
||||
Mail::raw('Tes email berhasil', function ($message) {
|
||||
$message->to('EMAIL_KAMU@gmail.com')
|
||||
->subject('Test Email Laravel');
|
||||
$message->to('EMAIL_KAMU@gmail.com')->subject('Test Email Laravel');
|
||||
});
|
||||
|
||||
return 'Email terkirim!';
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue