parent
3bdc385a6d
commit
9762288c38
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Nilai;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
|
||||||
|
class LaporanAkademikController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$nilai = Nilai::with(['siswa', 'mapel'])
|
||||||
|
->orderBy('id_siswa')
|
||||||
|
->get()
|
||||||
|
->groupBy('id_siswa');
|
||||||
|
|
||||||
|
return view('admin.laporan-akademik.index', compact('nilai'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cetak($idSiswa)
|
||||||
|
{
|
||||||
|
$nilai = Nilai::with(['siswa', 'mapel'])
|
||||||
|
->where('id_siswa', $idSiswa)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($nilai->isEmpty()) {
|
||||||
|
abort(404, 'Data nilai tidak ditemukan.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$siswa = $nilai->first()->siswa;
|
||||||
|
$rataRata = round($nilai->avg('nilai_akhir'), 2);
|
||||||
|
|
||||||
|
$pdf = Pdf::loadView('admin.laporan-akademik.pdf', compact(
|
||||||
|
'nilai',
|
||||||
|
'siswa',
|
||||||
|
'rataRata'
|
||||||
|
))->setPaper('A4', 'portrait');
|
||||||
|
|
||||||
|
return $pdf->stream('laporan-akademik-' . $siswa->nama . '.pdf');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers\guru;
|
namespace App\Http\Controllers\Guru;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
@ -11,134 +11,213 @@
|
||||||
|
|
||||||
class NilaiController extends Controller
|
class NilaiController extends Controller
|
||||||
{
|
{
|
||||||
// 📌 Tampilkan semua nilai
|
// ── INDEX: Tampilkan menu nilai guru ──────────────────────────────
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
$mapel_id = $user->mapel_id;
|
||||||
|
$kelas = Kelas::all();
|
||||||
|
|
||||||
// ambil mapel guru
|
// Filter kelas
|
||||||
$mapel_id = $user->mapel_id;
|
$query = Nilai::with(['siswa.kelas', 'mapel'])
|
||||||
|
->where('mapel_id', $mapel_id);
|
||||||
|
|
||||||
// ambil semua kelas (buat dropdown filter)
|
if ($request->kelas_id) {
|
||||||
$kelas = \App\Models\Kelas::all();
|
$query->whereHas('siswa', function ($q) use ($request) {
|
||||||
|
$q->where('kelas_id', $request->kelas_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// query nilai + relasi siswa & mapel
|
// Filter siswa (opsional)
|
||||||
$query = \App\Models\Nilai::with(['siswa.kelas', 'mapel'])
|
if ($request->siswa_id) {
|
||||||
->where('mapel_id', $mapel_id);
|
$query->where('siswa_id', $request->siswa_id);
|
||||||
|
}
|
||||||
|
|
||||||
// 🔥 FILTER KELAS
|
$nilai = $query->get();
|
||||||
if ($request->kelas_id) {
|
|
||||||
$query->whereHas('siswa', function ($q) use ($request) {
|
// Siswa berdasarkan kelas yang dipilih (untuk dropdown filter siswa)
|
||||||
$q->where('kelas_id', $request->kelas_id);
|
$siswaList = collect();
|
||||||
});
|
if ($request->kelas_id) {
|
||||||
|
$siswaList = Siswa::where('kelas_id', $request->kelas_id)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('guru.nilai.index', compact('nilai', 'kelas', 'siswaList'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$nilai = $query->get();
|
// ── HITUNG RATA-RATA: simpan nilai_akhir ke kolom rata ──────────
|
||||||
|
public function hitungRata(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'kelas_id' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
return view('guru.nilai.index', compact('nilai', 'kelas'));
|
$user = auth()->user();
|
||||||
}
|
$mapel_id = $user->mapel_id;
|
||||||
// 📌 Halaman input nilai per kelas
|
|
||||||
public function inputNilai()
|
|
||||||
{
|
|
||||||
$kelas = Kelas::all();
|
|
||||||
|
|
||||||
// 🔥 ambil mapel sesuai guru login
|
// Ambil semua nilai siswa di kelas ini untuk mapel guru
|
||||||
$mapel = MataPelajaran::find(auth()->user()->mapel_id);
|
$nilaiList = Nilai::with('siswa')
|
||||||
|
->where('mapel_id', $mapel_id)
|
||||||
|
->whereHas('siswa', fn($q) => $q->where('kelas_id', $request->kelas_id))
|
||||||
|
->get();
|
||||||
|
|
||||||
if (!$mapel) {
|
foreach ($nilaiList as $n) {
|
||||||
return back()->with('error', 'Mapel belum disetting!');
|
// Hitung rata-rata dari tugas, uts, uas
|
||||||
|
$parts = [];
|
||||||
|
if ($n->nilai_tugas !== null) $parts[] = (float) $n->nilai_tugas;
|
||||||
|
if ($n->nilai_uts !== null) $parts[] = (float) $n->nilai_uts;
|
||||||
|
if ($n->nilai_uas !== null) $parts[] = (float) $n->nilai_uas;
|
||||||
|
|
||||||
|
if (count($parts) > 0) {
|
||||||
|
$rata = round(array_sum($parts) / count($parts), 2);
|
||||||
|
$n->update(['nilai_akhir' => $rata]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('guru.nilai.index', ['kelas_id' => $request->kelas_id])
|
||||||
|
->with('success', 'Rata-rata berhasil dihitung!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('guru.nilai.input', compact('kelas', 'mapel'));
|
// ── KIRIM NILAI KE SISWA (per kelas atau semua) ─────────────────
|
||||||
}
|
public function kirimKeSiswa(Request $request)
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
$mapel_id = $user->mapel_id;
|
||||||
|
|
||||||
// 📌 Ambil siswa berdasarkan kelas (AJAX)
|
$query = Nilai::where('mapel_id', $mapel_id)
|
||||||
public function getSiswaByKelas($id)
|
->whereNotNull('nilai_akhir');
|
||||||
{
|
|
||||||
$siswa = Siswa::where('kelas_id', $id)->get();
|
|
||||||
|
|
||||||
return response()->json($siswa);
|
// Kalau ada kelas_id, kirim per kelas saja
|
||||||
}
|
if ($request->kelas_id) {
|
||||||
|
$query->whereHas('siswa', fn($q) => $q->where('kelas_id', $request->kelas_id));
|
||||||
|
}
|
||||||
|
|
||||||
// 📌 Simpan nilai banyak sekaligus
|
$nilaiList = $query->get();
|
||||||
|
|
||||||
|
foreach ($nilaiList as $n) {
|
||||||
|
$n->update(['sudah_kirim' => 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg = $request->kelas_id
|
||||||
|
? 'Nilai berhasil dikirim ke siswa kelas ini!'
|
||||||
|
: 'Nilai berhasil dikirim ke semua kelas!';
|
||||||
|
|
||||||
|
return redirect()->route('guru.nilai.index')
|
||||||
|
->with('success', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── HALAMAN INPUT NILAI PER KELAS ────────────────────────────────
|
||||||
|
public function inputNilai()
|
||||||
|
{
|
||||||
|
$kelas = Kelas::all();
|
||||||
|
$mapel = MataPelajaran::find(auth()->user()->mapel_id);
|
||||||
|
|
||||||
|
if (!$mapel) {
|
||||||
|
return back()->with('error', 'Mapel belum disetting!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('guru.nilai.input', compact('kelas', 'mapel'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AJAX: ambil siswa berdasarkan kelas ─────────────────────────
|
||||||
|
public function getSiswaByKelas($id)
|
||||||
|
{
|
||||||
|
$siswa = Siswa::where('kelas_id', $id)->get();
|
||||||
|
return response()->json($siswa);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SIMPAN NILAI BATCH ───────────────────────────────────────────
|
||||||
public function storeBatch(Request $request)
|
public function storeBatch(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'mapel_id' => 'required',
|
'mapel_id' => 'required',
|
||||||
'nilai' => 'required|array'
|
'nilai' => 'required|array',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($request->nilai as $siswa_id => $nilai) {
|
foreach ($request->nilai as $siswa_id => $nilai) {
|
||||||
|
|
||||||
if ($nilai != null) {
|
if ($nilai != null) {
|
||||||
Nilai::updateOrCreate(
|
Nilai::updateOrCreate(
|
||||||
[
|
['siswa_id' => $siswa_id, 'mapel_id' => $request->mapel_id],
|
||||||
'siswa_id' => $siswa_id,
|
['nilai' => $nilai]
|
||||||
'mapel_id' => $request->mapel_id
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'nilai' => $nilai
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('guru.nilai.index')
|
return redirect()->route('guru.nilai.index')
|
||||||
->with('success', 'Nilai berhasil disimpan');
|
->with('success', 'Nilai berhasil disimpan');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= CRUD BIASA =================
|
// ── MASUKKAN NILAI TUGAS DARI MENU TUGAS ─────────────────────────
|
||||||
|
// Dipanggil dari tombol di halaman tugas / rekap rata-rata
|
||||||
public function create()
|
public function masukkanNilaiTugas(Request $request)
|
||||||
{
|
|
||||||
$siswa = Siswa::all();
|
|
||||||
$mapel = MataPelajaran::all();
|
|
||||||
|
|
||||||
return view('guru.nilai.create', compact('siswa', 'mapel'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request)
|
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'siswa_id' => 'required',
|
'siswa_id' => 'required',
|
||||||
'mapel_id' => 'required',
|
'nilai_tugas' => 'required|numeric|min:0|max:100',
|
||||||
'nilai' => 'required|numeric|min:0|max:100',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Nilai::create($request->all());
|
$user = auth()->user();
|
||||||
|
$mapel_id = $user->mapel_id;
|
||||||
|
|
||||||
return redirect()->route('guru.nilai.index')
|
Nilai::updateOrCreate(
|
||||||
->with('success', 'Data nilai berhasil ditambahkan');
|
['siswa_id' => $request->siswa_id, 'mapel_id' => $mapel_id],
|
||||||
|
['nilai_tugas' => $request->nilai_tugas]
|
||||||
|
);
|
||||||
|
|
||||||
|
return back()->with('success', 'Nilai tugas berhasil dimasukkan ke menu nilai!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit($id)
|
// ── MASUKKAN NILAI UJIAN DARI MENU UJIAN ─────────────────────────
|
||||||
{
|
// Dipanggil dari tombol di menu ujian per ujian (UTS/UAS)
|
||||||
$nilai = Nilai::with(['siswa', 'mapel'])->findOrFail($id);
|
public function masukkanNilaiUjian(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'ujian_id' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
return view('guru.nilai.edit', compact('nilai'));
|
$ujian = \App\Models\Ujian::with('kelas')->findOrFail($request->ujian_id);
|
||||||
}
|
$mapel_id = $ujian->mapel_id;
|
||||||
|
$jenis = strtolower($ujian->jenis); // 'uts' atau 'uas'
|
||||||
|
$kolom = 'nilai_' . $jenis; // 'nilai_uts' atau 'nilai_uas'
|
||||||
|
|
||||||
|
// Ambil semua hasil ujian ini
|
||||||
|
$hasilList = \App\Models\Hasil::where('ujian_id', $ujian->id)->get();
|
||||||
|
|
||||||
|
foreach ($hasilList as $h) {
|
||||||
|
Nilai::updateOrCreate(
|
||||||
|
['siswa_id' => $h->siswa_id, 'mapel_id' => $mapel_id],
|
||||||
|
[$kolom => $h->nilai]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->with('success', 'Nilai ' . strtoupper($jenis) . ' berhasil dikirim ke menu nilai!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── EDIT ─────────────────────────────────────────────────────────
|
||||||
|
public function edit($id)
|
||||||
|
{
|
||||||
|
$nilai = Nilai::with(['siswa', 'mapel'])->findOrFail($id);
|
||||||
|
return view('guru.nilai.edit', compact('nilai'));
|
||||||
|
}
|
||||||
|
|
||||||
public function update(Request $request, $id)
|
public function update(Request $request, $id)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'nilai' => 'required|numeric|min:0|max:100'
|
'nilai_tugas' => 'nullable|numeric|min:0|max:100',
|
||||||
]);
|
'nilai_uts' => 'nullable|numeric|min:0|max:100',
|
||||||
|
'nilai_uas' => 'nullable|numeric|min:0|max:100',
|
||||||
|
]);
|
||||||
|
|
||||||
$nilai = Nilai::findOrFail($id);
|
$nilai = Nilai::findOrFail($id);
|
||||||
|
$nilai->update($request->only(['nilai_tugas', 'nilai_uts', 'nilai_uas']));
|
||||||
|
|
||||||
$nilai->update([
|
return redirect()->route('guru.nilai.index')
|
||||||
'nilai' => $request->nilai
|
->with('success', 'Nilai berhasil diupdate');
|
||||||
]);
|
}
|
||||||
|
|
||||||
return redirect()->route('guru.nilai.index')
|
|
||||||
->with('success', 'Nilai berhasil diupdate');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy($id)
|
public function destroy($id)
|
||||||
{
|
{
|
||||||
Nilai::findOrFail($id)->delete();
|
Nilai::findOrFail($id)->delete();
|
||||||
|
|
||||||
return redirect()->route('guru.nilai.index')
|
return redirect()->route('guru.nilai.index')
|
||||||
->with('success', 'Data nilai berhasil dihapus');
|
->with('success', 'Data nilai berhasil dihapus');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,91 +8,108 @@
|
||||||
use App\Models\Kelas;
|
use App\Models\Kelas;
|
||||||
use App\Models\MataPelajaran;
|
use App\Models\MataPelajaran;
|
||||||
use App\Models\PengumpulanTugas;
|
use App\Models\PengumpulanTugas;
|
||||||
use App\Models\Jadwal; // 🔥 WAJIB
|
use App\Models\Jadwal;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use App\Models\Siswa;
|
use App\Models\Siswa;
|
||||||
|
|
||||||
class TugasController extends Controller
|
class TugasController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$guru = auth()->user()->guru;
|
$guru = auth()->user()->guru;
|
||||||
|
|
||||||
$tugas = Tugas::with(['kelas', 'mapel'])
|
$tugas = Tugas::with(['kelas', 'mapel'])
|
||||||
->withCount('pengumpulan')
|
->withCount('pengumpulan')
|
||||||
->where('guru_id', $guru->id)
|
->where('guru_id', $guru->id)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
foreach ($tugas as $t) {
|
foreach ($tugas as $t) {
|
||||||
$t->total_siswa = Siswa::where('kelas_id', $t->kelas_id)->count();
|
$t->total_siswa = Siswa::where('kelas_id', $t->kelas_id)->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
$tugasIds = $tugas->pluck('id');
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Rekap nilai dikelompokkan per KELAS (untuk filter JS di view)
|
||||||
|
// Format: { "X IPA 1": [ {id, nama, inisial, jumlah_tugas, rata_rata}, … ], … }
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
$rekapPerKelas = \App\Models\PengumpulanTugas::with(['siswa', 'tugas.kelas'])
|
||||||
|
->whereIn('tugas_id', $tugasIds)
|
||||||
|
->whereNotNull('nilai')
|
||||||
|
->get()
|
||||||
|
->groupBy(function ($p) {
|
||||||
|
// Kelompokkan berdasarkan nama kelas siswa
|
||||||
|
return $p->tugas->kelas->nama_kelas ?? 'Tidak Diketahui';
|
||||||
|
})
|
||||||
|
->map(function ($kelasGroup) {
|
||||||
|
// Di dalam setiap kelas, kelompokkan lagi per siswa
|
||||||
|
return $kelasGroup
|
||||||
|
->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 [
|
||||||
|
'id' => $siswa->id ?? 0,
|
||||||
|
'nama' => $siswa->nama ?? '-',
|
||||||
|
'inisial' => strtoupper(substr($siswa->nama ?? 'S', 0, 1))
|
||||||
|
. (isset(explode(' ', $siswa->nama ?? '')[1])
|
||||||
|
? strtoupper(substr(explode(' ', $siswa->nama)[1], 0, 1))
|
||||||
|
: ''),
|
||||||
|
'jumlah_tugas' => $kumpulan->count(),
|
||||||
|
'rata_rata' => $rata,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values(); // reset key jadi array numerik
|
||||||
|
});
|
||||||
|
|
||||||
|
// Juga tetap sediakan $rekapNilai (flat) kalau masih dipakai view lain
|
||||||
|
$rekapNilai = $rekapPerKelas->flatten(1)->values();
|
||||||
|
|
||||||
|
return view('guru.tugas.index', compact('tugas', 'rekapNilai', 'rekapPerKelas'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ambil semua pengumpulan dari semua tugas guru ini
|
public function create()
|
||||||
$tugasIds = $tugas->pluck('id');
|
{
|
||||||
|
$kelas = Kelas::all();
|
||||||
|
$guru = auth()->user()->guru;
|
||||||
|
$mapel = $guru->mapelRel;
|
||||||
|
|
||||||
$rekapNilai = \App\Models\PengumpulanTugas::with('siswa')
|
return view('guru.tugas.create', compact('kelas', 'mapel'));
|
||||||
->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()
|
|
||||||
{
|
|
||||||
$kelas = Kelas::all();
|
|
||||||
$guru = auth()->user()->guru;
|
|
||||||
|
|
||||||
$mapel = $guru->mapelRel;
|
|
||||||
|
|
||||||
return view('guru.tugas.create', compact('kelas', 'mapel'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request)
|
|
||||||
{
|
|
||||||
$guru = auth()->user()->guru;
|
|
||||||
|
|
||||||
$request->validate([
|
|
||||||
'kelas_id' => 'required',
|
|
||||||
'judul' => 'required',
|
|
||||||
'deadline' => 'required',
|
|
||||||
'file' => 'nullable|mimes:pdf,doc,docx,ppt,pptx|max:2048'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$filePath = null;
|
|
||||||
|
|
||||||
// 🔥 INI BAGIAN PALING PENTING
|
|
||||||
if ($request->hasFile('file')) {
|
|
||||||
$filePath = $request->file('file')->store('tugas_file', 'public');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tugas::create([
|
public function store(Request $request)
|
||||||
'guru_id' => $guru->id,
|
{
|
||||||
'kelas_id' => $request->kelas_id,
|
$guru = auth()->user()->guru;
|
||||||
'mapel_id' => $guru->mapel_id,
|
|
||||||
'judul' => $request->judul,
|
$request->validate([
|
||||||
'deskripsi' => $request->deskripsi,
|
'kelas_id' => 'required',
|
||||||
'file' => $filePath,
|
'judul' => 'required',
|
||||||
'deadline' => str_replace('T', ' ', $request->deadline),
|
'deadline' => 'required',
|
||||||
]);
|
'file' => 'nullable|mimes:pdf,doc,docx,ppt,pptx|max:2048',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$filePath = null;
|
||||||
|
if ($request->hasFile('file')) {
|
||||||
|
$filePath = $request->file('file')->store('tugas_file', 'public');
|
||||||
|
}
|
||||||
|
|
||||||
|
Tugas::create([
|
||||||
|
'guru_id' => $guru->id,
|
||||||
|
'kelas_id' => $request->kelas_id,
|
||||||
|
'mapel_id' => $guru->mapel_id,
|
||||||
|
'judul' => $request->judul,
|
||||||
|
'deskripsi' => $request->deskripsi,
|
||||||
|
'file' => $filePath,
|
||||||
|
'deadline' => str_replace('T', ' ', $request->deadline),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('guru.tugas.index')
|
||||||
|
->with('success', 'Tugas berhasil dibuat');
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('guru.tugas.index')
|
|
||||||
->with('success', 'Tugas berhasil dibuat');
|
|
||||||
}
|
|
||||||
public function getMapelByKelas($id)
|
public function getMapelByKelas($id)
|
||||||
{
|
{
|
||||||
$guruId = auth()->user()->guru->id;
|
$guruId = auth()->user()->guru->id;
|
||||||
|
|
@ -106,94 +123,95 @@ public function getMapelByKelas($id)
|
||||||
|
|
||||||
return response()->json($mapel);
|
return response()->json($mapel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
$tugas = Tugas::with(['kelas', 'mapel'])->findOrFail($id);
|
$tugas = Tugas::with(['kelas', 'mapel'])->findOrFail($id);
|
||||||
|
|
||||||
return view('guru.tugas.show', compact('tugas'));
|
return view('guru.tugas.show', compact('tugas'));
|
||||||
}
|
|
||||||
public function edit($id)
|
|
||||||
{
|
|
||||||
$tugas = Tugas::findOrFail($id);
|
|
||||||
$kelas = Kelas::all();
|
|
||||||
|
|
||||||
return view('guru.tugas.edit', compact('tugas', 'kelas'));
|
|
||||||
}
|
|
||||||
public function update(Request $request, $id)
|
|
||||||
{
|
|
||||||
$request->validate([
|
|
||||||
'judul' => 'required',
|
|
||||||
'kelas_id' => 'required',
|
|
||||||
'deadline' => 'required'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$tugas = Tugas::findOrFail($id);
|
|
||||||
|
|
||||||
$tugas->update([
|
|
||||||
'judul' => $request->judul,
|
|
||||||
'kelas_id' => $request->kelas_id,
|
|
||||||
'deskripsi' => $request->deskripsi,
|
|
||||||
'deadline' => $request->deadline,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('guru.tugas.index')
|
|
||||||
->with('success', 'Tugas berhasil diupdate');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function download($id)
|
|
||||||
{
|
|
||||||
$tugas = Tugas::findOrFail($id);
|
|
||||||
|
|
||||||
if (!$tugas->file) {
|
|
||||||
abort(404, 'File tidak ditemukan');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = storage_path('app/public/' . $tugas->file);
|
public function edit($id)
|
||||||
|
{
|
||||||
|
$tugas = Tugas::findOrFail($id);
|
||||||
|
$kelas = Kelas::all();
|
||||||
|
|
||||||
if (!file_exists($path)) {
|
return view('guru.tugas.edit', compact('tugas', 'kelas'));
|
||||||
abort(404, 'File tidak ada di storage');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->download($path);
|
public function update(Request $request, $id)
|
||||||
}
|
{
|
||||||
|
$request->validate([
|
||||||
|
'judul' => 'required',
|
||||||
|
'kelas_id' => 'required',
|
||||||
|
'deadline' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
public function pengumpulan($id)
|
$tugas = Tugas::findOrFail($id);
|
||||||
{
|
|
||||||
$tugas = Tugas::with(['pengumpulan.siswa'])->findOrFail($id);
|
|
||||||
|
|
||||||
$deadline = Carbon::parse($tugas->deadline);
|
$tugas->update([
|
||||||
|
'judul' => $request->judul,
|
||||||
|
'kelas_id' => $request->kelas_id,
|
||||||
|
'deskripsi' => $request->deskripsi,
|
||||||
|
'deadline' => $request->deadline,
|
||||||
|
]);
|
||||||
|
|
||||||
foreach ($tugas->pengumpulan as $p) {
|
return redirect()->route('guru.tugas.index')
|
||||||
$waktu = Carbon::parse($p->created_at);
|
->with('success', 'Tugas berhasil diupdate');
|
||||||
$p->status = $waktu->gt($deadline) ? 'telat' : 'tepat';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Siapkan data untuk JS rata-rata
|
public function download($id)
|
||||||
$pengumpulanJson = $tugas->pengumpulan->map(function($p) {
|
{
|
||||||
return [
|
$tugas = Tugas::findOrFail($id);
|
||||||
'nama' => $p->siswa->nama ?? '-',
|
|
||||||
'inisial' => strtoupper(substr($p->siswa->nama ?? 'S', 0, 1)),
|
|
||||||
'nilai' => $p->nilai,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return view('guru.tugas.pengumpulan', compact('tugas', 'pengumpulanJson'));
|
if (!$tugas->file) {
|
||||||
}
|
abort(404, 'File tidak ditemukan');
|
||||||
|
}
|
||||||
public function nilai(Request $request, $id)
|
|
||||||
{
|
$path = storage_path('app/public/' . $tugas->file);
|
||||||
$request->validate([
|
|
||||||
'nilai' => 'required|integer|min:0|max:100',
|
if (!file_exists($path)) {
|
||||||
'komentar' => 'nullable'
|
abort(404, 'File tidak ada di storage');
|
||||||
]);
|
}
|
||||||
|
|
||||||
$pengumpulan = \App\Models\PengumpulanTugas::findOrFail($id);
|
return response()->download($path);
|
||||||
|
}
|
||||||
$pengumpulan->update([
|
|
||||||
'nilai' => $request->nilai,
|
public function pengumpulan($id)
|
||||||
'komentar' => $request->komentar
|
{
|
||||||
]);
|
$tugas = Tugas::with(['pengumpulan.siswa'])->findOrFail($id);
|
||||||
|
$deadline = Carbon::parse($tugas->deadline);
|
||||||
return back()->with('success', 'Nilai berhasil disimpan');
|
|
||||||
}
|
foreach ($tugas->pengumpulan as $p) {
|
||||||
|
$waktu = Carbon::parse($p->created_at);
|
||||||
|
$p->status = $waktu->gt($deadline) ? 'telat' : 'tepat';
|
||||||
|
}
|
||||||
|
|
||||||
|
$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)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'nilai' => 'required|integer|min:0|max:100',
|
||||||
|
'komentar' => 'nullable',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pengumpulan = \App\Models\PengumpulanTugas::findOrFail($id);
|
||||||
|
|
||||||
|
$pengumpulan->update([
|
||||||
|
'nilai' => $request->nilai,
|
||||||
|
'komentar' => $request->komentar,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Nilai berhasil disimpan');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,90 +5,115 @@
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Nilai;
|
use App\Models\Nilai;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class NilaiController extends Controller
|
class NilaiController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$siswa_id = auth()->user()->siswa_id;
|
$siswa_id = Auth::user()->siswa_id;
|
||||||
|
|
||||||
// 🔹 Ambil nilai tugas
|
// Cek apakah kolom sudah_kirim sudah ada di tabel nilai
|
||||||
$tugas = \App\Models\PengumpulanTugas::with('tugas.mapel')
|
$hasSudahKirim = Schema::hasColumn('nilai', 'sudah_kirim');
|
||||||
->where('siswa_id', $siswa_id)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
// 🔹 Ambil nilai ujian
|
$query = Nilai::with('mapel')
|
||||||
$ujian = \App\Models\Hasil::with('ujian.mapel')
|
->where('siswa_id', $siswa_id);
|
||||||
->where('siswa_id', $siswa_id)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
// 🔥 REKAP DATA
|
// Hanya filter sudah_kirim kalau kolomnya sudah ada
|
||||||
$data = [];
|
if ($hasSudahKirim) {
|
||||||
|
$query->where('sudah_kirim', 1);
|
||||||
|
}
|
||||||
|
|
||||||
// =======================
|
$nilaiGuru = $query->get();
|
||||||
// 🔹 PROSES NILAI TUGAS
|
|
||||||
// =======================
|
|
||||||
foreach ($tugas as $t) {
|
|
||||||
$mapel_id = $t->tugas->mapel->id ?? null;
|
|
||||||
|
|
||||||
if (!$mapel_id) continue;
|
// Rekap dari nilai guru
|
||||||
|
$data = [];
|
||||||
|
foreach ($nilaiGuru as $n) {
|
||||||
|
$mapel_id = $n->mapel_id;
|
||||||
|
if (!$mapel_id) continue;
|
||||||
|
|
||||||
|
// Kalau kolom sudah_kirim belum ada, tampilkan yang nilai_akhir-nya sudah terisi saja
|
||||||
|
if (!$hasSudahKirim && $n->nilai_akhir === null) continue;
|
||||||
|
|
||||||
if (!isset($data[$mapel_id])) {
|
|
||||||
$data[$mapel_id] = [
|
$data[$mapel_id] = [
|
||||||
'mapel' => $t->tugas->mapel->nama_mapel,
|
'mapel' => $n->mapel->nama_mapel ?? '—',
|
||||||
'tugas' => [],
|
'tugas' => $n->nilai_tugas !== null ? round($n->nilai_tugas) : null,
|
||||||
'uts' => null,
|
'uts' => $n->nilai_uts !== null ? round($n->nilai_uts) : null,
|
||||||
'uas' => null,
|
'uas' => $n->nilai_uas !== null ? round($n->nilai_uas) : null,
|
||||||
|
'rata' => $n->nilai_akhir !== null ? round($n->nilai_akhir) : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$data[$mapel_id]['tugas'][] = $t->nilai;
|
// Fallback ke sistem lama kalau belum ada data dari guru
|
||||||
|
if (empty($data)) {
|
||||||
|
$data = $this->getRekapsLama($siswa_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('siswa.nilai.index', compact('data'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// =======================
|
/**
|
||||||
// 🔹 PROSES NILAI UJIAN
|
* Fallback rekap dari PengumpulanTugas & Hasil (sistem lama)
|
||||||
// =======================
|
*/
|
||||||
foreach ($ujian as $u) {
|
private function getRekapsLama($siswa_id): array
|
||||||
$mapel_id = $u->ujian->mapel->id ?? null;
|
{
|
||||||
|
$tugas = \App\Models\PengumpulanTugas::with('tugas.mapel')
|
||||||
|
->where('siswa_id', $siswa_id)->get();
|
||||||
|
|
||||||
if (!$mapel_id) continue;
|
$ujian = \App\Models\Hasil::with('ujian.mapel')
|
||||||
|
->where('siswa_id', $siswa_id)->get();
|
||||||
|
|
||||||
if (!isset($data[$mapel_id])) {
|
$data = [];
|
||||||
$data[$mapel_id] = [
|
|
||||||
'mapel' => $u->ujian->mapel->nama_mapel,
|
foreach ($tugas as $t) {
|
||||||
'tugas' => [],
|
$mapel_id = $t->tugas->mapel->id ?? null;
|
||||||
'uts' => null,
|
if (!$mapel_id) continue;
|
||||||
'uas' => null,
|
if (!isset($data[$mapel_id])) {
|
||||||
|
$data[$mapel_id] = [
|
||||||
|
'mapel' => $t->tugas->mapel->nama_mapel,
|
||||||
|
'tugas_arr' => [],
|
||||||
|
'uts' => null,
|
||||||
|
'uas' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($t->nilai !== null) {
|
||||||
|
$data[$mapel_id]['tugas_arr'][] = $t->nilai;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_arr' => [],
|
||||||
|
'uts' => null,
|
||||||
|
'uas' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (strtoupper($u->ujian->jenis) === 'UTS') $data[$mapel_id]['uts'] = $u->nilai;
|
||||||
|
if (strtoupper($u->ujian->jenis) === 'UAS') $data[$mapel_id]['uas'] = $u->nilai;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($data as $k => $d) {
|
||||||
|
$rata_tugas = count($d['tugas_arr'])
|
||||||
|
? array_sum($d['tugas_arr']) / count($d['tugas_arr'])
|
||||||
|
: null;
|
||||||
|
$uts = $d['uts'] ?? null;
|
||||||
|
$uas = $d['uas'] ?? null;
|
||||||
|
$parts = array_filter([$rata_tugas, $uts, $uas], fn($v) => $v !== null);
|
||||||
|
$rata = count($parts) ? round(array_sum($parts) / count($parts)) : null;
|
||||||
|
|
||||||
|
$data[$k] = [
|
||||||
|
'mapel' => $d['mapel'],
|
||||||
|
'tugas' => $rata_tugas !== null ? round($rata_tugas) : null,
|
||||||
|
'uts' => $uts !== null ? round($uts) : null,
|
||||||
|
'uas' => $uas !== null ? round($uas) : null,
|
||||||
|
'rata' => $rata,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($u->ujian->jenis == 'UTS') {
|
return $data;
|
||||||
$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'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -11,14 +11,12 @@ class Nilai extends Model
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'siswa_id',
|
'siswa_id',
|
||||||
'mapel_id',
|
'mapel_id',
|
||||||
|
'nilai', // lama, jangan dihapus
|
||||||
// 🔥 lama (jangan dihapus dulu)
|
|
||||||
'nilai',
|
|
||||||
|
|
||||||
// 🔥 baru
|
|
||||||
'nilai_tugas',
|
'nilai_tugas',
|
||||||
'nilai_uts',
|
'nilai_uts',
|
||||||
'nilai_uas'
|
'nilai_uas',
|
||||||
|
'nilai_akhir', // hasil hitung rata-rata (disimpan permanen)
|
||||||
|
'sudah_kirim', // 0 = belum kirim ke siswa, 1 = sudah kirim
|
||||||
];
|
];
|
||||||
|
|
||||||
public function siswa()
|
public function siswa()
|
||||||
|
|
@ -31,19 +29,26 @@ public function mapel()
|
||||||
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
return $this->belongsTo(MataPelajaran::class, 'mapel_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔥 helper biar fleksibel (INI KUNCI AMAN)
|
/**
|
||||||
public function getNilaiAkhirAttribute()
|
* Accessor: hitung nilai akhir dinamis (kalau kolom nilai_akhir belum diisi)
|
||||||
|
*/
|
||||||
|
public function getNilaiAkhirDinamisAttribute()
|
||||||
{
|
{
|
||||||
// kalau pakai sistem baru
|
if ($this->nilai_akhir !== null) {
|
||||||
if ($this->nilai_tugas || $this->nilai_uts || $this->nilai_uas) {
|
return $this->nilai_akhir;
|
||||||
return collect([
|
|
||||||
$this->nilai_tugas,
|
|
||||||
$this->nilai_uts,
|
|
||||||
$this->nilai_uas
|
|
||||||
])->filter()->avg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback ke sistem lama
|
$parts = array_filter([
|
||||||
|
$this->nilai_tugas !== null ? (float) $this->nilai_tugas : null,
|
||||||
|
$this->nilai_uts !== null ? (float) $this->nilai_uts : null,
|
||||||
|
$this->nilai_uas !== null ? (float) $this->nilai_uas : null,
|
||||||
|
], fn($v) => $v !== null);
|
||||||
|
|
||||||
|
if (count($parts) > 0) {
|
||||||
|
return round(array_sum($parts) / count($parts), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback ke nilai lama
|
||||||
return $this->nilai;
|
return $this->nilai;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
|
"barryvdh/laravel-dompdf": "2.2",
|
||||||
"laravel/framework": "^11.31",
|
"laravel/framework": "^11.31",
|
||||||
"laravel/tinker": "^2.9",
|
"laravel/tinker": "^2.9",
|
||||||
"simplesoftwareio/simple-qrcode": "^4.2"
|
"simplesoftwareio/simple-qrcode": "^4.2"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "ba0b871c4e08ecaa364989ce5e6f5138",
|
"content-hash": "2135f2e9f15e9229e297b7ea4e12f164",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
|
|
@ -66,6 +66,89 @@
|
||||||
},
|
},
|
||||||
"time": "2022-12-07T17:46:57+00:00"
|
"time": "2022-12-07T17:46:57+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "barryvdh/laravel-dompdf",
|
||||||
|
"version": "v2.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/barryvdh/laravel-dompdf.git",
|
||||||
|
"reference": "c96f90c97666cebec154ca1ffb67afed372114d8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/c96f90c97666cebec154ca1ffb67afed372114d8",
|
||||||
|
"reference": "c96f90c97666cebec154ca1ffb67afed372114d8",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"dompdf/dompdf": "^2.0.7",
|
||||||
|
"illuminate/support": "^6|^7|^8|^9|^10|^11",
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"larastan/larastan": "^1.0|^2.7.0",
|
||||||
|
"orchestra/testbench": "^4|^5|^6|^7|^8|^9",
|
||||||
|
"phpro/grumphp": "^1 || ^2.5",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
|
||||||
|
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"Barryvdh\\DomPDF\\ServiceProvider"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Barryvdh\\DomPDF\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Barry vd. Heuvel",
|
||||||
|
"email": "barryvdh@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A DOMPDF Wrapper for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"dompdf",
|
||||||
|
"laravel",
|
||||||
|
"pdf"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
|
||||||
|
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v2.2.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://fruitcake.nl",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/barryvdh",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-04-25T13:16:04+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
"version": "0.12.3",
|
"version": "0.12.3",
|
||||||
|
|
@ -493,6 +576,74 @@
|
||||||
],
|
],
|
||||||
"time": "2024-02-05T11:56:58+00:00"
|
"time": "2024-02-05T11:56:58+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "dompdf/dompdf",
|
||||||
|
"version": "v2.0.8",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dompdf/dompdf.git",
|
||||||
|
"reference": "c20247574601700e1f7c8dab39310fca1964dc52"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/c20247574601700e1f7c8dab39310fca1964dc52",
|
||||||
|
"reference": "c20247574601700e1f7c8dab39310fca1964dc52",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"masterminds/html5": "^2.0",
|
||||||
|
"phenx/php-font-lib": ">=0.5.4 <1.0.0",
|
||||||
|
"phenx/php-svg-lib": ">=0.5.2 <1.0.0",
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8 || ^9",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-gd": "Needed to process images",
|
||||||
|
"ext-gmagick": "Improves image processing performance",
|
||||||
|
"ext-imagick": "Improves image processing performance",
|
||||||
|
"ext-zlib": "Needed for pdf stream compression"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dompdf\\": "src/"
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"lib/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "The Dompdf Community",
|
||||||
|
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
||||||
|
"homepage": "https://github.com/dompdf/dompdf",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/dompdf/dompdf/issues",
|
||||||
|
"source": "https://github.com/dompdf/dompdf/tree/v2.0.8"
|
||||||
|
},
|
||||||
|
"time": "2024-04-29T13:06:17+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dragonmantank/cron-expression",
|
"name": "dragonmantank/cron-expression",
|
||||||
"version": "v3.4.0",
|
"version": "v3.4.0",
|
||||||
|
|
@ -2121,6 +2272,79 @@
|
||||||
],
|
],
|
||||||
"time": "2024-12-08T08:18:47+00:00"
|
"time": "2024-12-08T08:18:47+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "masterminds/html5",
|
||||||
|
"version": "2.8.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Masterminds/html5-php.git",
|
||||||
|
"reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf",
|
||||||
|
"reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-dom": "*",
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.7-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Masterminds\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Matt Butcher",
|
||||||
|
"email": "technosophos@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Matt Farina",
|
||||||
|
"email": "matt@mattfarina.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Asmir Mustafic",
|
||||||
|
"email": "goetas@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An HTML5 parser and serializer.",
|
||||||
|
"homepage": "http://masterminds.github.io/html5-php",
|
||||||
|
"keywords": [
|
||||||
|
"HTML5",
|
||||||
|
"dom",
|
||||||
|
"html",
|
||||||
|
"parser",
|
||||||
|
"querypath",
|
||||||
|
"serializer",
|
||||||
|
"xml"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Masterminds/html5-php/issues",
|
||||||
|
"source": "https://github.com/Masterminds/html5-php/tree/2.8.1"
|
||||||
|
},
|
||||||
|
"time": "2023-05-10T11:58:31+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
|
|
@ -2625,6 +2849,108 @@
|
||||||
],
|
],
|
||||||
"time": "2025-05-08T08:14:37+00:00"
|
"time": "2025-05-08T08:14:37+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phenx/php-font-lib",
|
||||||
|
"version": "0.5.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dompdf/php-font-lib.git",
|
||||||
|
"reference": "a1681e9793040740a405ac5b189275059e2a9863"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a1681e9793040740a405ac5b189275059e2a9863",
|
||||||
|
"reference": "a1681e9793040740a405ac5b189275059e2a9863",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"FontLib\\": "src/FontLib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-or-later"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Ménager",
|
||||||
|
"email": "fabien.menager@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
||||||
|
"homepage": "https://github.com/PhenX/php-font-lib",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/dompdf/php-font-lib/issues",
|
||||||
|
"source": "https://github.com/dompdf/php-font-lib/tree/0.5.6"
|
||||||
|
},
|
||||||
|
"time": "2024-01-29T14:45:26+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phenx/php-svg-lib",
|
||||||
|
"version": "0.5.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dompdf/php-svg-lib.git",
|
||||||
|
"reference": "0e46722c154726a5f9ac218197ccc28adba16fcf"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/0e46722c154726a5f9ac218197ccc28adba16fcf",
|
||||||
|
"reference": "0e46722c154726a5f9ac218197ccc28adba16fcf",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"php": "^7.1 || ^8.0",
|
||||||
|
"sabberworm/php-css-parser": "^8.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Svg\\": "src/Svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-3.0-or-later"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Ménager",
|
||||||
|
"email": "fabien.menager@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A library to read, parse and export to PDF SVG files.",
|
||||||
|
"homepage": "https://github.com/PhenX/php-svg-lib",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/dompdf/php-svg-lib/issues",
|
||||||
|
"source": "https://github.com/dompdf/php-svg-lib/tree/0.5.3"
|
||||||
|
},
|
||||||
|
"time": "2024-02-23T20:39:24+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpoption/phpoption",
|
"name": "phpoption/phpoption",
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
|
|
@ -3388,6 +3714,78 @@
|
||||||
},
|
},
|
||||||
"time": "2025-06-25T14:20:11+00:00"
|
"time": "2025-06-25T14:20:11+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "sabberworm/php-css-parser",
|
||||||
|
"version": "v8.9.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
|
||||||
|
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9",
|
||||||
|
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41",
|
||||||
|
"rawr/cross-data-providers": "^2.0.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "for parsing UTF-8 CSS"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "9.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Sabberworm\\CSS\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Raphael Schweikert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Oliver Klee",
|
||||||
|
"email": "github@oliverklee.de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jake Hotson",
|
||||||
|
"email": "jake.github@qzdesign.co.uk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Parser for CSS Files written in PHP",
|
||||||
|
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
||||||
|
"keywords": [
|
||||||
|
"css",
|
||||||
|
"parser",
|
||||||
|
"stylesheet"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
|
||||||
|
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0"
|
||||||
|
},
|
||||||
|
"time": "2025-07-11T13:20:48+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "simplesoftwareio/simple-qrcode",
|
"name": "simplesoftwareio/simple-qrcode",
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?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(): void
|
||||||
|
{
|
||||||
|
Schema::table('nilai', function (Blueprint $table) {
|
||||||
|
$table->boolean('sudah_kirim')->default(false)->after('nilai_akhir');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('nilai', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('sudah_kirim');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
|
|
@ -1,173 +1,83 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
@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;--text-primary:#0f172a;--text-secondary:#475569;--surface:#ffffff;--surface-secondary:#f8fafc;--border:#e2e8f0;--shadow-md:0 4px 20px rgba(0,0,0,0.08);--radius-md:10px;--radius-lg:14px;--radius-xl:18px;}
|
||||||
:root {
|
*{font-family:'Plus Jakarta Sans',sans-serif;box-sizing:border-box;}
|
||||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
.page-wrapper{animation:fadeUp 0.4s ease both;}
|
||||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
|
||||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
.section-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-xl);box-shadow:var(--shadow-md);overflow:hidden;max-width:540px;margin:0 auto;}
|
||||||
--danger:#dc2626;
|
.card-header{padding:20px 24px;border-bottom:1px solid var(--border);background:var(--surface-secondary);}
|
||||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
.card-title{font-size:16px;font-weight:700;color:var(--text-primary);margin:0 0 2px;}
|
||||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
.card-sub{font-size:13px;color:var(--text-secondary);margin:0;}
|
||||||
--shadow-md:0 4px 20px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
|
.card-body{padding:24px;}
|
||||||
--shadow-focus:0 0 0 3px rgba(37,99,235,0.12);
|
.form-group{margin-bottom:18px;}
|
||||||
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
.form-label{display:block;font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:6px;}
|
||||||
}
|
.form-label span{font-size:11.5px;font-weight:400;color:var(--text-secondary);margin-left:6px;}
|
||||||
*{
|
.form-control{width:100%;padding:10px 14px;font-size:13.5px;font-family:'Plus Jakarta Sans',sans-serif;border:1px solid var(--border);border-radius:var(--radius-md);color:var(--text-primary);outline:none;transition:border-color .2s;}
|
||||||
box-sizing:border-box;
|
.form-control:focus{border-color:var(--primary);}
|
||||||
}
|
.form-footer{display:flex;gap:10px;align-items:center;padding-top:4px;}
|
||||||
|
.btn-save{display:inline-flex;align-items:center;gap:7px;padding:10px 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 .15s;box-shadow:0 2px 8px rgba(37,99,235,0.25);}
|
||||||
body,
|
.btn-save:hover{background:var(--primary-dark);transform:translateY(-1px);}
|
||||||
button,
|
.btn-back{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;font-size:13px;font-weight:600;font-family:'Plus Jakarta Sans',sans-serif;background:var(--surface-secondary);color:var(--text-secondary);border:1px solid var(--border);border-radius:var(--radius-md);text-decoration:none;transition:all .15s;}
|
||||||
input,
|
.btn-back:hover{background:var(--border);color:var(--text-primary);text-decoration:none;}
|
||||||
select,
|
.siswa-info{display:flex;align-items:center;gap:12px;background:var(--primary-light);border:1px solid var(--primary-border);border-radius:var(--radius-lg);padding:12px 16px;margin-bottom:22px;}
|
||||||
textarea{
|
.siswa-avatar{width:40px;height:40px;border-radius:50%;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;flex-shrink:0;}
|
||||||
font-family:'Plus Jakarta Sans',sans-serif;
|
.siswa-name{font-size:14px;font-weight:700;color:var(--text-primary);}
|
||||||
}
|
.siswa-kelas{font-size:12px;color:var(--text-secondary);margin-top:2px;}
|
||||||
.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>
|
</style>
|
||||||
|
|
||||||
<div class="edit-page">
|
<div class="container-fluid px-4 py-3 page-wrapper">
|
||||||
<div class="form-wrapper">
|
<div class="section-card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title">Edit Nilai Siswa</h5>
|
||||||
|
<p class="card-sub">Ubah nilai tugas, UTS, dan UAS</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{{-- Info siswa --}}
|
||||||
|
<div class="siswa-info">
|
||||||
|
<div class="siswa-avatar">{{ strtoupper(substr($nilai->siswa->nama ?? 'S', 0, 2)) }}</div>
|
||||||
|
<div>
|
||||||
|
<div class="siswa-name">{{ $nilai->siswa->nama ?? '—' }}</div>
|
||||||
|
<div class="siswa-kelas">{{ $nilai->siswa->kelas->nama_kelas ?? '—' }} · {{ $nilai->mapel->nama_mapel ?? '—' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a href="{{ route('guru.nilai.index') }}" class="back-nav">
|
<form action="{{ route('guru.nilai.update', $nilai->id) }}" method="POST">
|
||||||
<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
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="form-group">
|
||||||
<label class="form-label">Nama Siswa</label>
|
<label class="form-label">Nilai Tugas <span>(rata-rata semua tugas)</span></label>
|
||||||
<input type="text" class="form-control" value="{{ $nilai->siswa->nama }}" readonly>
|
<input type="number" name="nilai_tugas" class="form-control"
|
||||||
|
value="{{ old('nilai_tugas', $nilai->nilai_tugas) }}"
|
||||||
|
min="0" max="100" step="0.01" placeholder="0 – 100">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Mata Pelajaran</label>
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" value="{{ $nilai->mapel->nama_mapel }}" readonly>
|
<label class="form-label">Nilai UTS</label>
|
||||||
|
<input type="number" name="nilai_uts" class="form-control"
|
||||||
|
value="{{ old('nilai_uts', $nilai->nilai_uts) }}"
|
||||||
|
min="0" max="100" step="0.01" placeholder="0 – 100">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Nilai Baru <span style="color:var(--danger);">*</span></label>
|
<div class="form-group">
|
||||||
<div class="nilai-input-wrap">
|
<label class="form-label">Nilai UAS</label>
|
||||||
<input type="number" name="nilai" class="form-control" value="{{ $nilai->nilai }}" min="0" max="100" required>
|
<input type="number" name="nilai_uas" class="form-control"
|
||||||
<span class="nilai-unit">/ 100</span>
|
value="{{ old('nilai_uas', $nilai->nilai_uas) }}"
|
||||||
</div>
|
min="0" max="100" step="0.01" placeholder="0 – 100">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-footer">
|
||||||
|
<button type="submit" class="btn-save">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||||
|
Simpan Perubahan
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('guru.nilai.index') }}" class="btn-back">Batal</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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>
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -3,111 +3,142 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
--primary:#2563eb; --primary-light:#eff6ff; --primary-border:#bfdbfe; --primary-dark:#1d4ed8;
|
||||||
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
--success:#16a34a; --success-light:#f0fdf4; --success-border:#bbf7d0;
|
||||||
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
--warning:#d97706; --warning-light:#fffbeb; --warning-border:#fde68a;
|
||||||
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
--danger:#dc2626; --danger-light:#fef2f2; --danger-border:#fecaca;
|
||||||
|
--violet:#7c3aed; --violet-light:#f5f3ff; --violet-border:#ddd6fe;
|
||||||
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
--text-primary:#0f172a; --text-secondary:#475569; --text-muted:#94a3b8;
|
||||||
--surface:#ffffff; --surface-secondary:#f8fafc; --border:#e2e8f0;
|
--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-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;
|
--radius-sm:6px; --radius-md:10px; --radius-lg:14px; --radius-xl:18px;
|
||||||
}
|
}
|
||||||
* { font-family:'Plus Jakarta Sans',sans-serif; box-sizing:border-box; }
|
*{font-family:'Plus Jakarta Sans',sans-serif;box-sizing:border-box;}
|
||||||
.page-wrapper { animation:fadeUp 0.4s ease both; }
|
.page-wrapper{animation:fadeUp 0.4s ease both;}
|
||||||
@keyframes fadeUp { from{opacity:0;transform:translateY(12px)} to{opacity:1;transform:translateY(0)} }
|
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
|
||||||
|
|
||||||
/* HEADER */
|
/* HEADER */
|
||||||
.page-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:28px; gap:16px; flex-wrap:wrap; }
|
.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px;gap:16px;flex-wrap:wrap;}
|
||||||
.page-header-left { display:flex; align-items:center; gap:14px; }
|
.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{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-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-title{font-size:22px;font-weight:700;color:var(--text-primary);margin:0 0 2px;}
|
||||||
.page-subtitle { font-size:13px; color:var(--text-secondary); margin:0; }
|
.page-subtitle{font-size:13px;color:var(--text-secondary);margin:0;}
|
||||||
|
|
||||||
/* BUTTON ADD */
|
/* BUTTONS — ikon dikunci 14px agar tidak meluap */
|
||||||
.btn-add {
|
.btn-primary{display:inline-flex;align-items:center;gap:7px;padding:10px 18px;font-size:13px;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 .15s;box-shadow:0 2px 8px rgba(37,99,235,0.25);}
|
||||||
display:inline-flex; align-items:center; gap:7px; padding:10px 20px;
|
.btn-primary:hover{background:var(--primary-dark);color:#fff;text-decoration:none;transform:translateY(-1px);}
|
||||||
font-size:13.5px; font-weight:600; font-family:'Plus Jakarta Sans',sans-serif;
|
.btn-primary svg{width:14px;height:14px;flex-shrink:0;}
|
||||||
background:var(--primary); color:#fff; border:none; border-radius:var(--radius-md);
|
|
||||||
text-decoration:none; cursor:pointer; transition:all 0.15s;
|
.btn-success{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;font-size:13px;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 .15s;box-shadow:0 2px 8px rgba(22,163,74,0.25);}
|
||||||
box-shadow:0 2px 8px rgba(37,99,235,0.25);
|
.btn-success:hover{background:#15803d;transform:translateY(-1px);}
|
||||||
}
|
.btn-success svg{width:14px;height:14px;flex-shrink:0;}
|
||||||
.btn-add:hover { background:var(--primary-dark); color:#fff; text-decoration:none; transform:translateY(-1px); }
|
|
||||||
.btn-add svg { width:15px; height:15px; }
|
.btn-violet{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;font-size:13px;font-weight:600;font-family:'Plus Jakarta Sans',sans-serif;background:var(--violet);color:#fff;border:none;border-radius:var(--radius-md);cursor:pointer;transition:all .15s;box-shadow:0 2px 8px rgba(124,58,237,0.25);}
|
||||||
|
.btn-violet:hover{background:#6d28d9;transform:translateY(-1px);}
|
||||||
|
.btn-violet svg{width:14px;height:14px;flex-shrink:0;}
|
||||||
|
|
||||||
|
.btn-warning-sm{display:inline-flex;align-items:center;gap:5px;padding:5px 12px;font-size:12px;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;cursor:pointer;transition:all .15s;}
|
||||||
|
.btn-warning-sm:hover{background:#fef3c7;color:var(--warning);text-decoration:none;}
|
||||||
|
.btn-warning-sm svg{width:13px;height:13px;flex-shrink:0;}
|
||||||
|
|
||||||
|
/* TOAST ALERT — menggantikan alert full-page */
|
||||||
|
.toast-container{position:fixed;top:20px;right:20px;z-index:9999;display:flex;flex-direction:column;gap:10px;}
|
||||||
|
.toast{display:flex;align-items:center;gap:12px;padding:14px 18px;border-radius:var(--radius-lg);font-size:13.5px;font-weight:600;max-width:360px;box-shadow:0 8px 30px rgba(0,0,0,0.12);animation:toastIn 0.35s cubic-bezier(.34,1.56,.64,1) both;}
|
||||||
|
@keyframes toastIn{from{opacity:0;transform:translateX(60px)}to{opacity:1;transform:translateX(0)}}
|
||||||
|
.toast.toast-success{background:#fff;border:1px solid var(--success-border);color:var(--success);}
|
||||||
|
.toast-icon{width:32px;height:32px;border-radius:50%;background:var(--success-light);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
|
||||||
|
.toast-icon svg{width:16px;height:16px;color:var(--success);}
|
||||||
|
.toast-close{margin-left:auto;background:none;border:none;cursor:pointer;color:var(--text-muted);padding:2px;display:flex;align-items:center;border-radius:4px;transition:color .15s;}
|
||||||
|
.toast-close:hover{color:var(--text-primary);}
|
||||||
|
.toast-close svg{width:14px;height:14px;}
|
||||||
|
|
||||||
|
/* ALERT inline (fallback jika tidak pakai toast) */
|
||||||
|
.alert-success{display:flex;align-items:center;gap:10px;background:var(--success-light);border:1px solid var(--success-border);border-radius:var(--radius-md);padding:12px 16px;margin-bottom:18px;font-size:13px;font-weight:600;color:var(--success);}
|
||||||
|
.alert-success svg{width:16px;height:16px;flex-shrink:0;}
|
||||||
|
|
||||||
/* FILTER BAR */
|
/* FILTER BAR */
|
||||||
.filter-bar { display:flex; align-items:center; gap:12px; margin-bottom:0; }
|
.filter-row{display:flex;align-items:center;gap:10px;margin-bottom:18px;flex-wrap:wrap;}
|
||||||
.filter-select {
|
.filter-select{padding:8px 32px 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;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 .2s;}
|
||||||
padding:8px 34px 8px 12px; font-size:13px; font-family:'Plus Jakarta Sans',sans-serif;
|
.filter-select:focus{border-color:var(--primary);}
|
||||||
border:1px solid var(--border); border-radius:var(--radius-md);
|
.filter-label{font-size:13px;font-weight:600;color:var(--text-secondary);}
|
||||||
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 */
|
/* CARD */
|
||||||
.section-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden; }
|
.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-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; }
|
.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:10px;}
|
||||||
.toolbar-title { font-size:13.5px; font-weight:600; color:var(--text-primary); }
|
.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; margin-left: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;}
|
||||||
|
|
||||||
/* TABLE */
|
/* TABLE */
|
||||||
.table-responsive { overflow-x:auto; }
|
.table-responsive{overflow-x:auto;}
|
||||||
table.nilai-table { width:100%; border-collapse:collapse; min-width:600px; }
|
table.nilai-table{width:100%;border-collapse:collapse;min-width:700px;}
|
||||||
.nilai-table thead tr { background:var(--surface-secondary); border-bottom:1px solid var(--border); }
|
.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 thead th{padding:11px 16px;font-size:11px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.7px;text-align:left;white-space:nowrap;}
|
||||||
.nilai-table tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
.nilai-table thead th.c{text-align:center;}
|
||||||
.nilai-table tbody tr:last-child { border-bottom:none; }
|
.nilai-table tbody tr{border-bottom:1px solid var(--border);transition:background .15s;}
|
||||||
.nilai-table tbody tr:hover { background:#f8faff; }
|
.nilai-table tbody tr:last-child{border-bottom:none;}
|
||||||
.nilai-table tbody td { padding:14px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
.nilai-table tbody tr:hover{background:#f8faff;}
|
||||||
.cell-no { font-size:13px; color:var(--text-muted); font-weight:500; }
|
.nilai-table tbody td{padding:13px 16px;font-size:13.5px;color:var(--text-primary);vertical-align:middle;}
|
||||||
|
.nilai-table tbody td.c{text-align:center;}
|
||||||
|
|
||||||
/* NAME CELL */
|
/* BADGES */
|
||||||
.name-cell { display:flex; align-items:center; gap:10px; }
|
.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; }
|
.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;}
|
||||||
|
.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;}
|
||||||
/* NILAI BADGE */
|
.chip-nilai{display:inline-flex;align-items:center;justify-content:center;min-width:46px;padding:4px 10px;border-radius:var(--radius-sm);font-size:13px;font-weight:700;}
|
||||||
.nilai-chip {
|
.chip-a{background:var(--success-light);color:var(--success);border:1px solid var(--success-border);}
|
||||||
display:inline-flex; align-items:center; justify-content:center;
|
.chip-b{background:var(--primary-light);color:var(--primary);border:1px solid var(--primary-border);}
|
||||||
width:46px; height:28px; border-radius:var(--radius-sm);
|
.chip-c{background:var(--warning-light);color:var(--warning);border:1px solid var(--warning-border);}
|
||||||
font-size:13px; font-weight:700;
|
.chip-d{background:var(--danger-light);color:var(--danger);border:1px solid var(--danger-border);}
|
||||||
}
|
.chip-null{background:var(--surface-secondary);color:var(--text-muted);border:1px solid var(--border);}
|
||||||
.nilai-chip.tinggi { background:var(--success-light); color:var(--success); border:1px solid var(--success-border); }
|
.badge-kirim{background:var(--success-light);color:var(--success);border:1px solid var(--success-border);border-radius:999px;font-size:11px;font-weight:700;padding:2px 9px;display:inline-flex;align-items:center;gap:4px;}
|
||||||
.nilai-chip.sedang { background:var(--warning-light); color:var(--warning); border:1px solid var(--warning-border); }
|
.badge-kirim::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--success);}
|
||||||
.nilai-chip.rendah { background:var(--danger-light); color:var(--danger); border:1px solid var(--danger-border); }
|
.badge-belum{background:var(--surface-secondary);color:var(--text-muted);border:1px solid var(--border);border-radius:999px;font-size:11px;font-weight:600;padding:2px 9px;display:inline-flex;}
|
||||||
|
|
||||||
.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 */
|
||||||
.empty-state { padding:64px 24px; text-align:center; }
|
.empty-state{padding:56px 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{width:56px;height:56px;background:var(--surface-secondary);border-radius:50%;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;margin:0 auto 12px;}
|
||||||
.empty-icon svg { width:26px; height:26px; color:var(--text-muted); }
|
.empty-icon svg{width:24px;height:24px;color:var(--text-muted);}
|
||||||
.empty-title { font-size:15px; font-weight:600; color:var(--text-primary); margin-bottom:4px; }
|
.empty-title{font-size:15px;font-weight:600;color:var(--text-primary);margin-bottom:4px;}
|
||||||
.empty-desc { font-size:13px; color:var(--text-muted); }
|
.empty-desc{font-size:13px;color:var(--text-muted);}
|
||||||
|
|
||||||
/* TABLE FOOTER */
|
/* 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; }
|
.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;}
|
||||||
|
.table-footer .btn-violet svg{width:13px;height:13px;}
|
||||||
|
|
||||||
|
/* INFO BOX */
|
||||||
|
.info-box{display:flex;align-items:flex-start;gap:10px;background:var(--primary-light);border:1px solid var(--primary-border);border-radius:var(--radius-md);padding:12px 16px;margin-bottom:18px;font-size:13px;color:var(--primary);}
|
||||||
|
.info-box svg{width:16px;height:16px;flex-shrink:0;margin-top:1px;}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
{{-- TOAST ALERT (pojok kanan atas, auto-dismiss 4 detik) --}}
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="toast-container" id="toastContainer">
|
||||||
|
<div class="toast toast-success" id="toastSuccess">
|
||||||
|
<div class="toast-icon">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<span>{{ session('success') }}</span>
|
||||||
|
<button class="toast-close" onclick="dismissToast()">
|
||||||
|
<svg 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>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function dismissToast(){
|
||||||
|
var t=document.getElementById('toastSuccess');
|
||||||
|
if(t){t.style.transition='all .3s ease';t.style.opacity='0';t.style.transform='translateX(60px)';setTimeout(function(){t.remove();},300);}
|
||||||
|
}
|
||||||
|
setTimeout(dismissToast, 4000);
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="container-fluid px-4 py-2 page-wrapper">
|
<div class="container-fluid px-4 py-2 page-wrapper">
|
||||||
|
|
||||||
{{-- HEADER --}}
|
{{-- HEADER --}}
|
||||||
|
|
@ -119,35 +150,78 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="page-title">Data Nilai Siswa</h4>
|
<h4 class="page-title">Menu Nilai</h4>
|
||||||
<p class="page-subtitle">Kelola dan pantau nilai akademik siswa</p>
|
<p class="page-subtitle">Rekap nilai tugas, UTS, UAS & hitung rata-rata siswa</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ route('guru.nilai.input') }}" class="btn-add">
|
<a href="{{ route('guru.nilai.input') }}" class="btn-primary">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
<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>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
|
Input Nilai Manual
|
||||||
</svg>
|
|
||||||
Input Nilai
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- TABLE CARD --}}
|
{{-- INFO --}}
|
||||||
|
<div class="info-box">
|
||||||
|
<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>
|
||||||
|
<span>Nilai tugas & ujian otomatis masuk sini saat diklik "Masukkan ke Menu Nilai" di menu Tugas atau Ujian. Setelah semua terisi, pilih kelas → klik <strong>Hitung Rata-Rata</strong> → lalu <strong>Kirim ke Siswa</strong>.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- FILTER --}}
|
||||||
|
<form method="GET" action="{{ route('guru.nilai.index') }}" id="filterForm">
|
||||||
|
<div class="filter-row">
|
||||||
|
<span class="filter-label">Filter Kelas:</span>
|
||||||
|
<select name="kelas_id" class="filter-select" onchange="document.getElementById('filterForm').submit()" style="width:200px;">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
@if(request('kelas_id'))
|
||||||
|
<span class="filter-label" style="margin-left:10px;">Filter Siswa:</span>
|
||||||
|
<select name="siswa_id" class="filter-select" onchange="document.getElementById('filterForm').submit()" style="width:220px;">
|
||||||
|
<option value="">Semua Siswa</option>
|
||||||
|
@foreach($siswaList as $s)
|
||||||
|
<option value="{{ $s->id }}" {{ request('siswa_id') == $s->id ? 'selected' : '' }}>{{ $s->nama }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(request('kelas_id') || request('siswa_id'))
|
||||||
|
<a href="{{ route('guru.nilai.index') }}" style="font-size:12.5px;color:var(--text-muted);font-weight:600;text-decoration:none;">✕ Reset</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{-- TABEL NILAI --}}
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<div class="card-toolbar">
|
<div class="card-toolbar">
|
||||||
<span class="toolbar-title">Daftar Nilai<span class="toolbar-badge">{{ $nilai->count() }} data</span></span>
|
<span class="toolbar-title">
|
||||||
<form method="GET" action="{{ route('guru.nilai.index') }}" style="margin:0;">
|
Daftar Nilai
|
||||||
<div class="filter-bar">
|
<span class="toolbar-badge">{{ $nilai->count() }} data</span>
|
||||||
<span class="filter-label">Filter:</span>
|
</span>
|
||||||
<select name="kelas_id" class="filter-select" onchange="this.form.submit()">
|
@if(request('kelas_id'))
|
||||||
<option value="">Semua Kelas</option>
|
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
||||||
@foreach($kelas as $k)
|
{{-- TOMBOL HITUNG RATA-RATA --}}
|
||||||
<option value="{{ $k->id }}" {{ request('kelas_id') == $k->id ? 'selected' : '' }}>
|
<form action="{{ route('guru.nilai.hitungRata') }}" method="POST">
|
||||||
{{ $k->nama_kelas }}
|
@csrf
|
||||||
</option>
|
<input type="hidden" name="kelas_id" value="{{ request('kelas_id') }}">
|
||||||
@endforeach
|
<button type="submit" class="btn-success" onclick="return confirm('Hitung rata-rata nilai untuk kelas ini?')">
|
||||||
</select>
|
<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>
|
Hitung Rata-Rata
|
||||||
</form>
|
</button>
|
||||||
|
</form>
|
||||||
|
{{-- TOMBOL KIRIM KE KELAS INI --}}
|
||||||
|
<form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="kelas_id" value="{{ request('kelas_id') }}">
|
||||||
|
<button type="submit" class="btn-violet" onclick="return confirm('Kirim nilai ke siswa kelas ini? Siswa akan bisa melihat nilai mereka.')">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/></svg>
|
||||||
|
Kirim ke Kelas Ini
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
|
@ -157,15 +231,25 @@
|
||||||
<th>No</th>
|
<th>No</th>
|
||||||
<th>Nama Siswa</th>
|
<th>Nama Siswa</th>
|
||||||
<th>Kelas</th>
|
<th>Kelas</th>
|
||||||
<th>Mata Pelajaran</th>
|
<th class="c">Nilai Tugas</th>
|
||||||
<th>Nilai</th>
|
<th class="c">Nilai UTS</th>
|
||||||
<th style="text-align:center;">Aksi</th>
|
<th class="c">Nilai UAS</th>
|
||||||
|
<th class="c">Rata-Rata</th>
|
||||||
|
<th class="c">Status Kirim</th>
|
||||||
|
<th class="c">Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@forelse($nilai as $n)
|
@forelse($nilai as $n)
|
||||||
|
@php
|
||||||
|
$rata = $n->nilai_akhir;
|
||||||
|
$rataClass = !$rata ? 'chip-null' : ($rata >= 85 ? 'chip-a' : ($rata >= 70 ? 'chip-b' : ($rata >= 55 ? 'chip-c' : 'chip-d')));
|
||||||
|
$tugasClass = !$n->nilai_tugas ? 'chip-null' : ($n->nilai_tugas >= 85 ? 'chip-a' : ($n->nilai_tugas >= 70 ? 'chip-b' : ($n->nilai_tugas >= 55 ? 'chip-c' : 'chip-d')));
|
||||||
|
$utsClass = !$n->nilai_uts ? 'chip-null' : ($n->nilai_uts >= 85 ? 'chip-a' : ($n->nilai_uts >= 70 ? 'chip-b' : ($n->nilai_uts >= 55 ? 'chip-c' : 'chip-d')));
|
||||||
|
$uasClass = !$n->nilai_uas ? 'chip-null' : ($n->nilai_uas >= 85 ? 'chip-a' : ($n->nilai_uas >= 70 ? 'chip-b' : ($n->nilai_uas >= 55 ? 'chip-c' : 'chip-d')));
|
||||||
|
@endphp
|
||||||
<tr>
|
<tr>
|
||||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
<td style="font-size:12.5px;color:var(--text-muted);">{{ $loop->iteration }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="name-cell">
|
<div class="name-cell">
|
||||||
<div class="name-avatar">{{ strtoupper(substr($n->siswa->nama ?? 'S', 0, 2)) }}</div>
|
<div class="name-avatar">{{ strtoupper(substr($n->siswa->nama ?? 'S', 0, 2)) }}</div>
|
||||||
|
|
@ -173,32 +257,33 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td><span class="badge-kelas">{{ $n->siswa->kelas->nama_kelas ?? '—' }}</span></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 class="c"><span class="chip-nilai {{ $tugasClass }}">{{ $n->nilai_tugas ?? '—' }}</span></td>
|
||||||
<td>
|
<td class="c"><span class="chip-nilai {{ $utsClass }}">{{ $n->nilai_uts ?? '—' }}</span></td>
|
||||||
<span class="nilai-chip {{ $n->nilai >= 80 ? 'tinggi' : ($n->nilai >= 60 ? 'sedang' : 'rendah') }}">
|
<td class="c"><span class="chip-nilai {{ $uasClass }}">{{ $n->nilai_uas ?? '—' }}</span></td>
|
||||||
{{ $n->nilai }}
|
<td class="c"><span class="chip-nilai {{ $rataClass }}">{{ $rata ?? '—' }}</span></td>
|
||||||
</span>
|
<td class="c">
|
||||||
|
@if($n->sudah_kirim)
|
||||||
|
<span class="badge-kirim">Sudah Dikirim</span>
|
||||||
|
@else
|
||||||
|
<span class="badge-belum">Belum</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align:center;">
|
<td class="c">
|
||||||
<a href="{{ route('guru.nilai.edit', $n->id) }}" class="btn-edit-sm">
|
<a href="{{ route('guru.nilai.edit', $n->id) }}" class="btn-warning-sm">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<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>
|
||||||
<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
|
Edit
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">
|
<td colspan="9">
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
<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>
|
||||||
<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>
|
||||||
<div class="empty-title">Belum ada data nilai</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 class="empty-desc">Masukkan nilai dari menu Tugas atau Ujian terlebih dahulu.</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -207,9 +292,20 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- FOOTER + TOMBOL KIRIM SEMUA KELAS --}}
|
||||||
<div class="table-footer">
|
<div class="table-footer">
|
||||||
<span>Menampilkan {{ $nilai->count() }} data nilai</span>
|
<span>{{ $nilai->count() }} data nilai</span>
|
||||||
<span>{{ now()->format('d M Y') }}</span>
|
<div style="display:flex;align-items:center;gap:10px;">
|
||||||
|
<span>{{ now()->format('d M Y') }}</span>
|
||||||
|
<form action="{{ route('guru.nilai.kirimKeSiswa') }}" method="POST" style="margin:0;">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn-violet" style="padding:7px 14px;font-size:12.5px;"
|
||||||
|
onclick="return confirm('Kirim nilai ke SEMUA kelas? Siswa di semua kelas akan bisa melihat nilai mereka.')">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/></svg>
|
||||||
|
Kirim ke Semua Kelas
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,91 +85,191 @@
|
||||||
|
|
||||||
.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); }
|
.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); }
|
||||||
|
|
||||||
/* RATA-RATA SECTION */
|
/* ===== FILTER RATA-RATA ===== */
|
||||||
.btn-hitung-rata {
|
.filter-section { margin-top:24px; }
|
||||||
display: inline-flex; align-items: center; gap: 8px;
|
.filter-card {
|
||||||
padding: 10px 22px; border-radius: var(--radius-md); border: none;
|
background:var(--surface); border:1px solid var(--border);
|
||||||
background: var(--success); color: #fff;
|
border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden;
|
||||||
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); }
|
.filter-card-header {
|
||||||
.btn-hitung-rata svg { width: 15px; height: 15px; }
|
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;
|
||||||
|
}
|
||||||
|
.filter-header-left { display:flex; align-items:center; gap:12px; }
|
||||||
|
.filter-header-icon {
|
||||||
|
width:38px; height:38px; border-radius:var(--radius-md);
|
||||||
|
background:var(--primary-light); border:1px solid var(--primary-border);
|
||||||
|
display:flex; align-items:center; justify-content:center; flex-shrink:0;
|
||||||
|
}
|
||||||
|
.filter-header-icon svg { width:18px; height:18px; color:var(--primary); }
|
||||||
|
.filter-header-title { font-size:14px; font-weight:700; color:var(--text-primary); margin:0; }
|
||||||
|
.filter-header-sub { font-size:12px; color:var(--text-secondary); margin:2px 0 0; }
|
||||||
|
|
||||||
.rata-section { margin-top: 20px; animation: fadeUp 0.3s ease both; }
|
.btn-hitung-rata2 {
|
||||||
|
display:inline-flex; align-items:center; gap:7px;
|
||||||
|
padding:10px 20px; 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,.22);
|
||||||
|
}
|
||||||
|
.btn-hitung-rata2:hover { background:#15803d; transform:translateY(-1px); }
|
||||||
|
.btn-hitung-rata2 svg { width:15px; height:15px; }
|
||||||
|
|
||||||
.rata-card {
|
.filter-body { padding:18px 22px; }
|
||||||
background: var(--surface); border: 1px solid var(--border);
|
.filter-select-all-row {
|
||||||
border-radius: var(--radius-xl); box-shadow: var(--shadow-md); overflow: hidden;
|
display:flex; align-items:center; justify-content:space-between;
|
||||||
|
margin-bottom:14px; flex-wrap:wrap; gap:8px;
|
||||||
}
|
}
|
||||||
.rata-card-toolbar {
|
.filter-select-all-row span { font-size:13px; color:var(--text-secondary); font-weight:500; }
|
||||||
display: flex; align-items: center; justify-content: space-between;
|
.filter-quick-links { display:flex; gap:14px; }
|
||||||
padding: 16px 22px; border-bottom: 1px solid var(--border);
|
.filter-quick-link {
|
||||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
font-size:12.5px; color:var(--primary); cursor:pointer;
|
||||||
|
background:none; border:none; font-family:'Plus Jakarta Sans',sans-serif;
|
||||||
|
padding:0; font-weight:600;
|
||||||
}
|
}
|
||||||
.rata-toolbar-left { display: flex; align-items: center; gap: 10px; }
|
.filter-quick-link:hover { text-decoration:underline; }
|
||||||
.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; }
|
|
||||||
|
|
||||||
table.rata-table { width: 100%; border-collapse: collapse; min-width: 600px; }
|
.kelas-group { margin-bottom:12px; border:1px solid var(--border); border-radius:var(--radius-lg); overflow:hidden; }
|
||||||
.rata-table thead tr {
|
.kelas-group:last-child { margin-bottom:0; }
|
||||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
.kelas-group-header {
|
||||||
border-bottom: 2px solid #bbf7d0;
|
display:flex; align-items:center; gap:10px; padding:10px 16px;
|
||||||
|
background:var(--surface-secondary); border-bottom:1px solid var(--border);
|
||||||
|
cursor:pointer; user-select:none;
|
||||||
}
|
}
|
||||||
.rata-table thead th {
|
.kelas-group-header:hover { background:#f1f5f9; }
|
||||||
padding: 12px 16px; font-size: 11px; font-weight: 700;
|
.kelas-group-header input[type=checkbox] {
|
||||||
color: #166534; text-transform: uppercase; letter-spacing: .7px;
|
width:16px; height:16px; accent-color:var(--primary); cursor:pointer; flex-shrink:0;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.rata-table thead th.center { text-align: center; }
|
.kelas-label-name { font-size:13.5px; font-weight:700; color:var(--text-primary); flex:1; }
|
||||||
.rata-table tbody tr { border-bottom: 1px solid var(--border); transition: background .15s; }
|
.kelas-count-badge {
|
||||||
.rata-table tbody tr:last-child { border-bottom: none; }
|
background:var(--primary-light); color:var(--primary);
|
||||||
.rata-table tbody tr:hover { background: #f0fdf4; }
|
border:1px solid var(--primary-border); border-radius:999px;
|
||||||
.rata-table tbody td { padding: 14px 16px; font-size: 13.5px; color: var(--text-primary); vertical-align: middle; }
|
font-size:11.5px; font-weight:600; padding:2px 9px;
|
||||||
.rata-table tbody td.center { text-align: center; }
|
}
|
||||||
|
.kelas-avg-preview {
|
||||||
|
font-size:12px; color:var(--text-muted); font-weight:500;
|
||||||
|
}
|
||||||
|
.kelas-toggle-btn {
|
||||||
|
width:24px; height:24px; border:none; background:none;
|
||||||
|
cursor:pointer; display:flex; align-items:center; justify-content:center;
|
||||||
|
color:var(--text-muted); font-size:12px; border-radius:4px;
|
||||||
|
transition:background 0.15s; flex-shrink:0;
|
||||||
|
}
|
||||||
|
.kelas-toggle-btn:hover { background:var(--border); }
|
||||||
|
|
||||||
.rata-avatar-row { display: flex; align-items: center; gap: 10px; }
|
.siswa-list-wrapper { padding:0; }
|
||||||
|
.siswa-item {
|
||||||
|
display:flex; align-items:center; gap:10px;
|
||||||
|
padding:10px 16px; border-bottom:1px solid var(--border); cursor:pointer;
|
||||||
|
transition:background 0.12s; user-select:none;
|
||||||
|
}
|
||||||
|
.siswa-item:last-child { border-bottom:none; }
|
||||||
|
.siswa-item:hover { background:#f8faff; }
|
||||||
|
.siswa-item input[type=checkbox] {
|
||||||
|
width:15px; height:15px; accent-color:var(--primary); cursor:pointer; flex-shrink:0;
|
||||||
|
}
|
||||||
|
.siswa-avatar {
|
||||||
|
width:32px; height:32px; 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:800; color:var(--primary); flex-shrink:0;
|
||||||
|
}
|
||||||
|
.siswa-nama-text { font-size:13px; font-weight:600; color:var(--text-primary); flex:1; }
|
||||||
|
.siswa-tugas-text { font-size:12px; color:var(--text-muted); margin-right:4px; }
|
||||||
|
.chip-nilai-sm {
|
||||||
|
display:inline-flex; padding:3px 10px; border-radius:999px;
|
||||||
|
font-size:11.5px; font-weight:700;
|
||||||
|
}
|
||||||
|
.chip-a { background:#dcfce7; color:#166534; }
|
||||||
|
.chip-b { background:var(--primary-light); color:var(--primary); }
|
||||||
|
.chip-c { background:var(--warning-light); color:var(--warning); }
|
||||||
|
.chip-d { background:var(--danger-light); color:var(--danger); }
|
||||||
|
|
||||||
|
/* ===== RESULT TABLE ===== */
|
||||||
|
.rata-result-section { margin-top:20px; animation:fadeUp 0.3s ease both; }
|
||||||
|
.rata-result-card {
|
||||||
|
background:var(--surface); border:1px solid var(--border);
|
||||||
|
border-radius:var(--radius-xl); box-shadow:var(--shadow-md); overflow:hidden;
|
||||||
|
}
|
||||||
|
.rata-result-header {
|
||||||
|
padding:16px 22px; border-bottom:1px solid var(--border);
|
||||||
|
background:linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||||
|
display:flex; align-items:center; gap:10px;
|
||||||
|
}
|
||||||
|
.rata-result-icon {
|
||||||
|
width:34px; height:34px; border-radius:9px;
|
||||||
|
background:var(--success); color:#fff;
|
||||||
|
display:flex; align-items:center; justify-content:center; flex-shrink:0;
|
||||||
|
}
|
||||||
|
.rata-result-icon svg { width:16px; height:16px; }
|
||||||
|
.rata-result-title { font-size:13.5px; font-weight:700; color:#166534; margin:0; }
|
||||||
|
.rata-result-sub { font-size:11.5px; color:#15803d; margin:2px 0 0; }
|
||||||
|
|
||||||
|
.kelas-result-block { border-bottom:1px solid var(--border); }
|
||||||
|
.kelas-result-block:last-of-type { border-bottom:none; }
|
||||||
|
.kelas-result-header {
|
||||||
|
padding:10px 20px; background:var(--surface-secondary);
|
||||||
|
display:flex; align-items:center; justify-content:space-between;
|
||||||
|
border-bottom:1px solid var(--border);
|
||||||
|
}
|
||||||
|
.kelas-result-name {
|
||||||
|
font-size:11px; font-weight:700; color:var(--text-muted);
|
||||||
|
text-transform:uppercase; letter-spacing:0.6px;
|
||||||
|
}
|
||||||
|
.kelas-result-avg {
|
||||||
|
display:inline-flex; align-items:center; gap:6px;
|
||||||
|
font-size:12.5px; font-weight:700;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.rata-result-table { width:100%; border-collapse:collapse; min-width:560px; }
|
||||||
|
.rata-result-table thead tr { background:linear-gradient(135deg, #f0fdf4, #dcfce7); border-bottom:2px solid #bbf7d0; }
|
||||||
|
.rata-result-table thead th {
|
||||||
|
padding:11px 16px; font-size:11px; font-weight:700;
|
||||||
|
color:#166534; text-transform:uppercase; letter-spacing:.6px; white-space:nowrap;
|
||||||
|
}
|
||||||
|
.rata-result-table thead th.c { text-align:center; }
|
||||||
|
.rata-result-table tbody tr { border-bottom:1px solid var(--border); transition:background .15s; }
|
||||||
|
.rata-result-table tbody tr:last-child { border-bottom:none; }
|
||||||
|
.rata-result-table tbody tr:hover { background:#f0fdf4; }
|
||||||
|
.rata-result-table tbody td { padding:13px 16px; font-size:13.5px; color:var(--text-primary); vertical-align:middle; }
|
||||||
|
.rata-result-table tbody td.c { text-align:center; }
|
||||||
|
|
||||||
|
.rata-avatar-row { display:flex; align-items:center; gap:10px; }
|
||||||
.rata-avatar {
|
.rata-avatar {
|
||||||
width: 36px; height: 36px; border-radius: 50%;
|
width:34px; height:34px; border-radius:50%;
|
||||||
background: var(--success-light); border: 2px solid #bbf7d0;
|
background:var(--success-light); border:1.5px solid var(--success-border);
|
||||||
display: flex; align-items: center; justify-content: center;
|
display:flex; align-items:center; justify-content:center;
|
||||||
font-size: 13px; font-weight: 800; color: var(--success); flex-shrink: 0;
|
font-size:12px; font-weight:800; color:var(--success); flex-shrink:0;
|
||||||
}
|
}
|
||||||
.rata-nama { font-weight: 700; color: var(--text-primary); font-size: 13.5px; }
|
.chip-nilai-lg {
|
||||||
|
display:inline-flex; padding:5px 16px; border-radius:999px;
|
||||||
.chip-nilai {
|
font-size:14px; font-weight:800;
|
||||||
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 {
|
.chip-predikat {
|
||||||
display: inline-flex; align-items: center; gap: 4px;
|
display:inline-flex; padding:4px 11px; border-radius:999px;
|
||||||
padding: 4px 12px; border-radius: 999px; font-size: 11.5px; font-weight: 700;
|
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 {
|
.rata-result-footer {
|
||||||
display: flex; align-items: center; justify-content: space-between;
|
display:flex; align-items:center; justify-content:space-between;
|
||||||
padding: 13px 22px; border-top: 1px solid var(--border);
|
padding:13px 22px; border-top:1px solid var(--border);
|
||||||
background: var(--surface-secondary); font-size: 13px; color: var(--text-muted);
|
background:var(--surface-secondary); font-size:13px; color:var(--text-muted);
|
||||||
}
|
}
|
||||||
.rata-footer-nilai { font-size: 16px; font-weight: 800; color: var(--text-primary); }
|
.rata-result-footer-val { font-size:16px; font-weight:800; color:var(--text-primary); }
|
||||||
|
.rata-empty { padding:40px 24px; text-align:center; font-size:13px; color:var(--text-muted); }
|
||||||
|
|
||||||
|
/* ===== TOMBOL MASUKKAN KE MENU NILAI (baru) ===== */
|
||||||
|
.btn-masuk-nilai {
|
||||||
|
display:inline-flex; align-items:center; gap:5px;
|
||||||
|
padding:5px 12px; font-size:11.5px; font-weight:700;
|
||||||
|
font-family:'Plus Jakarta Sans',sans-serif;
|
||||||
|
background:var(--primary-light); color:var(--primary);
|
||||||
|
border:1px solid var(--primary-border);
|
||||||
|
border-radius:6px; cursor:pointer; transition:all .15s;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.btn-masuk-nilai:hover { background:#dbeafe; transform:translateY(-1px); }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid px-4 py-2 page-wrapper">
|
<div class="container-fluid px-4 py-2 page-wrapper">
|
||||||
|
|
@ -192,6 +292,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- TABEL DAFTAR TUGAS --}}
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<div class="card-toolbar">
|
<div class="card-toolbar">
|
||||||
<span class="toolbar-title">Daftar Tugas<span class="toolbar-badge">{{ $tugas->count() }} tugas</span></span>
|
<span class="toolbar-title">Daftar Tugas<span class="toolbar-badge">{{ $tugas->count() }} tugas</span></span>
|
||||||
|
|
@ -279,103 +380,330 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- TOMBOL HITUNG --}}
|
{{-- ===== FILTER RATA-RATA PER KELAS & SISWA ===== --}}
|
||||||
<div style="text-align:right">
|
<div class="filter-section">
|
||||||
<button class="btn-hitung-rata" onclick="tampilRataRata()">
|
<div class="filter-card">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
<div class="filter-card-header">
|
||||||
<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"/>
|
<div class="filter-header-left">
|
||||||
</svg>
|
<div class="filter-header-icon">
|
||||||
Hitung Rata-Rata Nilai Siswa
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
</button>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2a1 1 0 01-.293.707L13 13.414V19a1 1 0 01-.553.894l-4 2A1 1 0 017 21v-7.586L3.293 6.707A1 1 0 013 6V4z"/>
|
||||||
</div>
|
</svg>
|
||||||
|
</div>
|
||||||
{{-- TABEL RATA-RATA --}}
|
<div>
|
||||||
<div id="rata-section" class="rata-section" style="display:none">
|
<p class="filter-header-title">Filter Perhitungan Rata-Rata Nilai</p>
|
||||||
<div class="rata-card">
|
<p class="filter-header-sub">Pilih kelas dan siswa yang ingin dihitung rata-rata nilainya</p>
|
||||||
<div class="rata-card-toolbar">
|
</div>
|
||||||
<div class="rata-toolbar-left">
|
</div>
|
||||||
<div class="rata-toolbar-icon">
|
<button class="btn-hitung-rata2" onclick="hitungRataRata()">
|
||||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2">
|
<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"/>
|
<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>
|
</svg>
|
||||||
|
Hitung Rata-Rata
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="filter-body">
|
||||||
|
<div class="filter-select-all-row">
|
||||||
|
<span>Centang siswa yang ingin dihitung:</span>
|
||||||
|
<div class="filter-quick-links">
|
||||||
|
<button class="filter-quick-link" onclick="selectAllSiswa(true)">Pilih Semua</button>
|
||||||
|
<button class="filter-quick-link" onclick="selectAllSiswa(false)">Hapus Semua</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="filter-kelas-list">
|
||||||
<p class="rata-toolbar-title">Rekap Rata-Rata Nilai per Siswa</p>
|
{{-- Dibangun oleh JS dari data PHP --}}
|
||||||
<p class="rata-toolbar-sub">Dihitung dari semua tugas yang sudah dinilai guru</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
|
{{-- TABEL HASIL RATA-RATA --}}
|
||||||
|
<div id="rata-result-section" class="rata-result-section" style="display:none"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- DATA REKAP DARI PHP → JS --}}
|
||||||
<script>
|
<script>
|
||||||
function tampilRataRata() {
|
const REKAP_DATA = @json($rekapPerKelas);
|
||||||
const sec = document.getElementById('rata-section');
|
const CSRF_TOKEN = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||||
|
|
||||||
|
function getChipClass(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';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFilter() {
|
||||||
|
const container = document.getElementById('filter-kelas-list');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const entries = Object.entries(REKAP_DATA);
|
||||||
|
if (entries.length === 0) {
|
||||||
|
container.innerHTML = '<p style="text-align:center;padding:24px;font-size:13px;color:var(--text-muted)">Belum ada siswa yang mendapatkan nilai.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.forEach(([kelas, siswaList]) => {
|
||||||
|
if (!siswaList || siswaList.length === 0) return;
|
||||||
|
|
||||||
|
const avgKelas = siswaList.length
|
||||||
|
? Math.round(siswaList.reduce((s, x) => s + parseFloat(x.rata_rata), 0) / siswaList.length)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const grp = document.createElement('div');
|
||||||
|
grp.className = 'kelas-group';
|
||||||
|
grp.dataset.kelas = kelas;
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'kelas-group-header';
|
||||||
|
|
||||||
|
const cbKelas = document.createElement('input');
|
||||||
|
cbKelas.type = 'checkbox';
|
||||||
|
cbKelas.checked = true;
|
||||||
|
cbKelas.className = 'cb-kelas';
|
||||||
|
cbKelas.dataset.kelas = kelas;
|
||||||
|
cbKelas.addEventListener('change', (e) => {
|
||||||
|
grp.querySelectorAll('.cb-siswa').forEach(cb => { cb.checked = e.target.checked; });
|
||||||
|
});
|
||||||
|
|
||||||
|
const labelName = document.createElement('span');
|
||||||
|
labelName.className = 'kelas-label-name';
|
||||||
|
labelName.textContent = kelas;
|
||||||
|
|
||||||
|
const badge = document.createElement('span');
|
||||||
|
badge.className = 'kelas-count-badge';
|
||||||
|
badge.textContent = siswaList.length + ' siswa';
|
||||||
|
|
||||||
|
const avgPreview = document.createElement('span');
|
||||||
|
avgPreview.className = 'kelas-avg-preview';
|
||||||
|
avgPreview.textContent = 'Rata kelas: ' + avgKelas;
|
||||||
|
|
||||||
|
const toggleBtn = document.createElement('button');
|
||||||
|
toggleBtn.className = 'kelas-toggle-btn';
|
||||||
|
toggleBtn.innerHTML = '▲';
|
||||||
|
toggleBtn.title = 'Tampilkan/sembunyikan';
|
||||||
|
|
||||||
|
header.append(cbKelas, labelName, badge, avgPreview, toggleBtn);
|
||||||
|
|
||||||
|
const listWrap = document.createElement('div');
|
||||||
|
listWrap.className = 'siswa-list-wrapper';
|
||||||
|
|
||||||
|
toggleBtn.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const hidden = listWrap.style.display === 'none';
|
||||||
|
listWrap.style.display = hidden ? 'block' : 'none';
|
||||||
|
toggleBtn.innerHTML = hidden ? '▲' : '▼';
|
||||||
|
});
|
||||||
|
|
||||||
|
header.addEventListener('click', (e) => {
|
||||||
|
if (e.target === cbKelas || e.target === toggleBtn) return;
|
||||||
|
toggleBtn.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
siswaList.forEach(s => {
|
||||||
|
const cls = getChipClass(parseFloat(s.rata_rata));
|
||||||
|
const item = document.createElement('label');
|
||||||
|
item.className = 'siswa-item';
|
||||||
|
item.style.cursor = 'pointer';
|
||||||
|
|
||||||
|
const cbSiswa = document.createElement('input');
|
||||||
|
cbSiswa.type = 'checkbox';
|
||||||
|
cbSiswa.checked = true;
|
||||||
|
cbSiswa.className = 'cb-siswa';
|
||||||
|
cbSiswa.dataset.siswaId = s.id;
|
||||||
|
cbSiswa.dataset.kelas = kelas;
|
||||||
|
cbSiswa.addEventListener('change', () => {
|
||||||
|
const all = [...grp.querySelectorAll('.cb-siswa')];
|
||||||
|
const cbK = grp.querySelector('.cb-kelas');
|
||||||
|
const allChecked = all.every(cb => cb.checked);
|
||||||
|
const someChecked = all.some(cb => cb.checked);
|
||||||
|
cbK.checked = allChecked;
|
||||||
|
cbK.indeterminate = !allChecked && someChecked;
|
||||||
|
});
|
||||||
|
|
||||||
|
const avatar = document.createElement('div');
|
||||||
|
avatar.className = 'siswa-avatar';
|
||||||
|
avatar.textContent = s.inisial;
|
||||||
|
|
||||||
|
const nama = document.createElement('span');
|
||||||
|
nama.className = 'siswa-nama-text';
|
||||||
|
nama.textContent = s.nama;
|
||||||
|
|
||||||
|
const tugasInfo = document.createElement('span');
|
||||||
|
tugasInfo.className = 'siswa-tugas-text';
|
||||||
|
tugasInfo.textContent = s.jumlah_tugas + ' tugas';
|
||||||
|
|
||||||
|
const chip = document.createElement('span');
|
||||||
|
chip.className = 'chip-nilai-sm chip-' + cls;
|
||||||
|
chip.textContent = parseFloat(s.rata_rata).toFixed(1);
|
||||||
|
|
||||||
|
// ── TOMBOL MASUKKAN KE MENU NILAI (baru) ──────────────
|
||||||
|
const formMasuk = document.createElement('form');
|
||||||
|
formMasuk.action = '/guru/nilai/masukkan-tugas';
|
||||||
|
formMasuk.method = 'POST';
|
||||||
|
formMasuk.style.cssText = 'display:inline-flex;flex-shrink:0;';
|
||||||
|
formMasuk.innerHTML = `
|
||||||
|
<input type="hidden" name="_token" value="${CSRF_TOKEN}">
|
||||||
|
<input type="hidden" name="siswa_id" value="${s.id}">
|
||||||
|
<input type="hidden" name="nilai_tugas" value="${parseFloat(s.rata_rata).toFixed(2)}">
|
||||||
|
<button type="submit" class="btn-masuk-nilai"
|
||||||
|
onclick="event.stopPropagation(); return confirm('Masukkan nilai tugas ${s.nama.replace(/'/g,"\\'")} (${parseFloat(s.rata_rata).toFixed(1)}) ke Menu Nilai?')">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.2" style="width:11px;height:11px;">
|
||||||
|
<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 2v10"/>
|
||||||
|
</svg>
|
||||||
|
→ Menu Nilai
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
item.append(cbSiswa, avatar, nama, tugasInfo, chip, formMasuk);
|
||||||
|
listWrap.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
grp.appendChild(header);
|
||||||
|
grp.appendChild(listWrap);
|
||||||
|
container.appendChild(grp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllSiswa(val) {
|
||||||
|
document.querySelectorAll('.cb-siswa, .cb-kelas').forEach(cb => {
|
||||||
|
cb.checked = val;
|
||||||
|
cb.indeterminate = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hitungRataRata() {
|
||||||
|
const selected = {};
|
||||||
|
document.querySelectorAll('.cb-siswa:checked').forEach(cb => {
|
||||||
|
const kelas = cb.dataset.kelas;
|
||||||
|
const id = parseInt(cb.dataset.siswaId);
|
||||||
|
if (!selected[kelas]) selected[kelas] = [];
|
||||||
|
selected[kelas].push(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const sec = document.getElementById('rata-result-section');
|
||||||
|
|
||||||
|
if (Object.keys(selected).length === 0) {
|
||||||
|
sec.innerHTML = '<div class="rata-result-card"><div class="rata-empty">Pilih minimal satu siswa terlebih dahulu.</div></div>';
|
||||||
|
sec.style.display = 'block';
|
||||||
|
sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = '<div class="rata-result-card">';
|
||||||
|
html += `<div class="rata-result-header">
|
||||||
|
<div class="rata-result-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-result-title">Rekap Rata-Rata Nilai per Siswa</p>
|
||||||
|
<p class="rata-result-sub">Dihitung dari semua tugas yang sudah dinilai, dikelompokkan per kelas</p>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const allAvgs = [];
|
||||||
|
let totalSiswa = 0;
|
||||||
|
let totalKelas = 0;
|
||||||
|
|
||||||
|
Object.entries(REKAP_DATA).forEach(([kelas, siswaList]) => {
|
||||||
|
const selectedIds = selected[kelas] || [];
|
||||||
|
const filtered = siswaList
|
||||||
|
.filter(s => selectedIds.includes(parseInt(s.id)))
|
||||||
|
.sort((a, b) => parseFloat(b.rata_rata) - parseFloat(a.rata_rata));
|
||||||
|
|
||||||
|
if (filtered.length === 0) return;
|
||||||
|
|
||||||
|
totalKelas++;
|
||||||
|
totalSiswa += filtered.length;
|
||||||
|
|
||||||
|
const kelasAvgArr = filtered.map(s => parseFloat(s.rata_rata));
|
||||||
|
const kelasAvg = Math.round(kelasAvgArr.reduce((a, b) => a + b, 0) / kelasAvgArr.length);
|
||||||
|
allAvgs.push(...kelasAvgArr);
|
||||||
|
|
||||||
|
const cls = getChipClass(kelasAvg);
|
||||||
|
|
||||||
|
html += `<div class="kelas-result-block">
|
||||||
|
<div class="kelas-result-header">
|
||||||
|
<span class="kelas-result-name">${kelas} · ${filtered.length} siswa</span>
|
||||||
|
<span class="kelas-result-avg">
|
||||||
|
Rata-rata kelas:
|
||||||
|
<span class="chip-nilai-sm chip-${cls}" style="font-size:13px">${kelasAvg}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="rata-result-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:32px">#</th>
|
||||||
|
<th>Nama Siswa</th>
|
||||||
|
<th class="c">Tugas Dinilai</th>
|
||||||
|
<th class="c">Rata-Rata Nilai</th>
|
||||||
|
<th class="c">Predikat</th>
|
||||||
|
<th class="c">Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>`;
|
||||||
|
|
||||||
|
filtered.forEach((s, i) => {
|
||||||
|
const rataS = Math.round(parseFloat(s.rata_rata) * 10) / 10;
|
||||||
|
const clsS = getChipClass(rataS);
|
||||||
|
html += `<tr>
|
||||||
|
<td style="font-size:12px;color:var(--text-muted)">${i + 1}</td>
|
||||||
|
<td>
|
||||||
|
<div class="rata-avatar-row">
|
||||||
|
<div class="rata-avatar">${s.inisial}</div>
|
||||||
|
<span style="font-weight:600">${s.nama}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="c" style="color:var(--text-secondary)">${s.jumlah_tugas} tugas</td>
|
||||||
|
<td class="c"><span class="chip-nilai-lg chip-${clsS}">${rataS}</span></td>
|
||||||
|
<td class="c"><span class="chip-predikat chip-${clsS}">${getPredikat(rataS)}</span></td>
|
||||||
|
<td class="c">
|
||||||
|
<form action="/guru/nilai/masukkan-tugas" method="POST" style="display:inline;">
|
||||||
|
<input type="hidden" name="_token" value="${CSRF_TOKEN}">
|
||||||
|
<input type="hidden" name="siswa_id" value="${s.id}">
|
||||||
|
<input type="hidden" name="nilai_tugas" value="${rataS}">
|
||||||
|
<button type="submit" class="btn-masuk-nilai"
|
||||||
|
onclick="return confirm('Masukkan nilai tugas ${s.nama.replace(/'/g,"\\'")} (${rataS}) ke Menu Nilai?')">
|
||||||
|
→ Menu Nilai
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `</tbody></table></div></div>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalAvg = allAvgs.length
|
||||||
|
? (Math.round(allAvgs.reduce((a, b) => a + b, 0) / allAvgs.length * 10) / 10)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
html += `<div class="rata-result-footer">
|
||||||
|
<span>${totalSiswa} siswa dari ${totalKelas} kelas</span>
|
||||||
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
|
<span>Rata-rata keseluruhan:</span>
|
||||||
|
<span class="rata-result-footer-val">${totalAvg} / 100</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
sec.innerHTML = html;
|
||||||
sec.style.display = 'block';
|
sec.style.display = 'block';
|
||||||
sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', buildFilter);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</div>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -33,6 +33,9 @@
|
||||||
.sw-btn-success:hover{background:#047857;color:#fff;transform:translateY(-1px);}
|
.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{background:var(--info-light);color:var(--info);border:1px solid #BAE6FD;}
|
||||||
.sw-btn-info:hover{background:var(--info);color:#fff;}
|
.sw-btn-info:hover{background:var(--info);color:#fff;}
|
||||||
|
/* Tombol baru: kirim ke menu nilai */
|
||||||
|
.sw-btn-nilai{background:#f5f3ff;color:#7c3aed;border:1px solid #ddd6fe;font-size:12px;}
|
||||||
|
.sw-btn-nilai:hover{background:#7c3aed;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{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{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-left{display:flex;align-items:center;gap:10px;}
|
||||||
|
|
@ -54,6 +57,7 @@
|
||||||
.sw-jenis-uas{background:var(--warning-light);border:1px solid #FDE68A;color:var(--warning);}
|
.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-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-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-status-nilai{display:inline-flex;align-items:center;gap:5px;background:#f5f3ff;border:1px solid #ddd6fe;border-radius:99px;padding:3px 10px;font-size:11px;font-weight:700;color:#7c3aed;}
|
||||||
.sw-ujian-actions{display:flex;gap:8px;align-items:center;flex-shrink:0;flex-wrap:wrap;}
|
.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{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-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);}
|
||||||
|
|
@ -63,7 +67,6 @@
|
||||||
/* NILAI SECTION */
|
/* 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-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-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{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-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{width:100%;border-collapse:collapse;}
|
||||||
|
|
@ -163,7 +166,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sw-ujian-actions">
|
<div class="sw-ujian-actions">
|
||||||
{{-- Tombol Kirim (hanya kalau masih draft) --}}
|
{{-- Tombol Kirim ke Siswa (hanya kalau masih draft) --}}
|
||||||
@if($u->status_kirim == 'draft')
|
@if($u->status_kirim == 'draft')
|
||||||
<form action="{{ route('guru.ujian.kirim', $u->id) }}" method="POST" class="d-inline"
|
<form action="{{ route('guru.ujian.kirim', $u->id) }}" method="POST" class="d-inline"
|
||||||
onsubmit="return confirm('Kirim ujian ini ke semua kelas yang dipilih?')">
|
onsubmit="return confirm('Kirim ujian ini ke semua kelas yang dipilih?')">
|
||||||
|
|
@ -175,6 +178,19 @@
|
||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
{{-- ✅ Tombol Kirim ke Menu Nilai (hanya kalau sudah terkirim & ada hasil) --}}
|
||||||
|
@if($u->status_kirim == 'terkirim')
|
||||||
|
<form action="{{ route('guru.nilai.masukkanUjian') }}" method="POST" class="d-inline"
|
||||||
|
onsubmit="return confirm('Kirim nilai {{ $u->jenis }} ujian ini ke Menu Nilai? Nilai siswa yang sudah mengerjakan akan masuk ke rekap nilai.')">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="ujian_id" value="{{ $u->id }}">
|
||||||
|
<button type="submit" class="sw-btn sw-btn-sm sw-btn-nilai">
|
||||||
|
<svg width="13" height="13" 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 2v10"/></svg>
|
||||||
|
→ Menu Nilai ({{ $u->jenis }})
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
|
||||||
<a href="{{ route('guru.ujian.edit', $u->id) }}" class="sw-btn sw-btn-sm sw-btn-warning">
|
<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>
|
<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
|
Edit
|
||||||
|
|
|
||||||
|
|
@ -6,486 +6,180 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--ink: #0c0f1a;
|
--ink:#0c0f1a; --text-sec:#475569; --text-muted:#94a3b8;
|
||||||
--text-sec: #475569;
|
--surface:#ffffff; --surface-2:#f6f8fc; --surface-3:#eef1f8; --border:#e2e8f0;
|
||||||
--text-muted: #94a3b8;
|
--blue:#2563eb; --blue-dark:#1d4ed8; --blue-soft:#eff6ff; --blue-border:#bfdbfe;
|
||||||
--surface: #ffffff;
|
--green:#059669; --green-soft:#ecfdf5; --green-border:#a7f3d0;
|
||||||
--surface-2: #f6f8fc;
|
--amber:#d97706; --amber-soft:#fffbeb; --amber-border:#fde68a;
|
||||||
--surface-3: #eef1f8;
|
--violet:#7c3aed; --violet-soft:#f5f3ff; --violet-border:#ddd6fe;
|
||||||
--border: #e2e8f0;
|
--red:#dc2626; --red-soft:#fef2f2; --red-border:#fecaca;
|
||||||
|
--r-md:10px; --r-lg:16px; --r-xl:22px; --r-2xl:28px;
|
||||||
--blue: #2563eb;
|
--shadow-card:0 4px 20px rgba(12,15,26,0.07), 0 1px 4px rgba(12,15,26,0.04);
|
||||||
--blue-dark: #1d4ed8;
|
|
||||||
--blue-soft: #eff6ff;
|
|
||||||
--blue-border: #bfdbfe;
|
|
||||||
|
|
||||||
--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; }
|
||||||
|
.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)} }
|
||||||
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
/* 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; }
|
||||||
|
.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; }
|
||||||
|
|
||||||
.nilai-page {
|
/* SUMMARY */
|
||||||
font-family: 'Sora', sans-serif;
|
.summary-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-bottom:22px; }
|
||||||
padding: 28px 28px 52px;
|
.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 .2s,transform .2s; }
|
||||||
background: var(--surface-2);
|
.sum-card:hover { transform:translateY(-2px); }
|
||||||
min-height: 100vh;
|
.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; }
|
||||||
|
|
||||||
/* ── ANIMATIONS ───────────────────────────────────── */
|
/* TABLE CARD */
|
||||||
.nilai-page > * { animation: rise 0.45s ease both; }
|
.table-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r-xl); box-shadow:var(--shadow-card); overflow:hidden; }
|
||||||
.nilai-page > *:nth-child(1) { animation-delay: 0s; }
|
.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; }
|
||||||
.nilai-page > *:nth-child(2) { animation-delay: 0.07s; }
|
.toolbar-title { font-size:14px; font-weight:700; color:var(--ink); display:flex; align-items:center; gap:10px; }
|
||||||
.nilai-page > *:nth-child(3) { animation-delay: 0.14s; }
|
.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; }
|
||||||
.nilai-page > *:nth-child(4) { animation-delay: 0.20s; }
|
.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; }
|
||||||
|
|
||||||
@keyframes rise {
|
/* TABLE */
|
||||||
from { opacity: 0; transform: translateY(14px); }
|
.table-responsive { overflow-x:auto; }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
table.nilai-table { width:100%; border-collapse:collapse; min-width:680px; }
|
||||||
}
|
.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 .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; }
|
||||||
|
|
||||||
/* ── HERO BANNER ──────────────────────────────────── */
|
.mapel-cell { display:flex; align-items:center; gap:10px; }
|
||||||
.page-hero {
|
.mapel-dot { width:8px; height:8px; border-radius:50%; background:var(--blue); flex-shrink:0; }
|
||||||
background: var(--ink);
|
.mapel-name { font-weight:600; color: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 {
|
.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; }
|
||||||
content: '';
|
.chip.tugas { background:var(--blue-soft); color:var(--blue); border:1px solid var(--blue-border); }
|
||||||
position: absolute; inset: 0;
|
.chip.uts { background:var(--amber-soft); color:var(--amber); border:1px solid var(--amber-border); }
|
||||||
background:
|
.chip.uas { background:var(--green-soft); color:var(--green); border:1px solid var(--green-border); }
|
||||||
radial-gradient(ellipse 55% 90% at 100% 50%, rgba(37,99,235,0.22) 0%, transparent 65%),
|
.chip.rata-a { background:var(--green-soft); color:var(--green); border:1px solid var(--green-border); }
|
||||||
radial-gradient(ellipse 35% 60% at 0% 0%, rgba(124,58,237,0.13) 0%, transparent 55%);
|
.chip.rata-b { background:var(--blue-soft); color:var(--blue); border:1px solid var(--blue-border); }
|
||||||
pointer-events: none;
|
.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); }
|
||||||
|
.chip.null { background:var(--surface-3); color:var(--text-muted); border:1px solid var(--border); }
|
||||||
|
|
||||||
.hero-left { position: relative; z-index: 1; }
|
.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); }
|
||||||
|
|
||||||
.hero-tag {
|
/* BELUM TERSEDIA */
|
||||||
display: inline-flex; align-items: center; gap: 7px;
|
.badge-pending { display:inline-flex; align-items:center; gap:5px; background:var(--surface-3); border:1px solid var(--border); border-radius:999px; padding:3px 10px; font-size:11.5px; font-weight:600; color:var(--text-muted); }
|
||||||
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 */
|
||||||
.empty-state {
|
.empty-state { padding:64px 24px; text-align:center; }
|
||||||
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); }
|
||||||
|
|
||||||
.empty-icon {
|
.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; }
|
||||||
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); }
|
@media(max-width:1024px){ .summary-grid{grid-template-columns:repeat(2,1fr);} }
|
||||||
.empty-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
|
@media(max-width:768px){ .nilai-page{padding:16px 16px 48px;} .hero-right{display:none;} .summary-grid{grid-template-columns:repeat(2,1fr);gap:10px;} }
|
||||||
.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>
|
</style>
|
||||||
|
|
||||||
<div class="nilai-page">
|
<div class="nilai-page">
|
||||||
|
|
||||||
{{-- ── HERO ──────────────────────────────────────── --}}
|
{{-- HERO --}}
|
||||||
<div class="page-hero">
|
<div class="page-hero">
|
||||||
<div class="hero-left">
|
<div class="hero-left">
|
||||||
<div class="hero-tag">
|
<div class="hero-tag"><span class="hero-tag-dot"></span>Rekap Akademik</div>
|
||||||
<span class="hero-tag-dot"></span>
|
<h1 class="hero-title">Data <em>Nilai Saya</em></h1>
|
||||||
Rekap Akademik
|
<p class="hero-sub">Nilai tugas, UTS, UAS, dan rata-rata per mata pelajaran yang dikirim guru</p>
|
||||||
</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>
|
||||||
<div class="hero-right">
|
<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
|
@php
|
||||||
$avgAll = collect($data)->avg('rata');
|
$mapelCount = count($data);
|
||||||
|
$avgAll = collect($data)->avg('rata');
|
||||||
@endphp
|
@endphp
|
||||||
<div class="hero-stat">
|
<div class="hero-stat">
|
||||||
<div class="hero-stat-val">{{ $avgAll ? number_format($avgAll, 1) : '—' }}</div>
|
<div class="hero-stat-val">{{ $mapelCount }}</div>
|
||||||
|
<div class="hero-stat-lbl">Mata Pelajaran</div>
|
||||||
|
</div>
|
||||||
|
<div class="hero-stat">
|
||||||
|
<div class="hero-stat-val">{{ $avgAll ? number_format($avgAll,1) : '—' }}</div>
|
||||||
<div class="hero-stat-lbl">Rata-rata</div>
|
<div class="hero-stat-lbl">Rata-rata</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- ── SUMMARY CARDS ──────────────────────────────── --}}
|
{{-- SUMMARY --}}
|
||||||
@php
|
@php
|
||||||
$avgTugas = collect($data)->whereNotNull('tugas')->avg('tugas');
|
$avgTugas = collect($data)->where('tugas', '!=', null)->avg('tugas');
|
||||||
$avgUts = collect($data)->whereNotNull('uts')->avg('uts');
|
$avgUts = collect($data)->where('uts', '!=', null)->avg('uts');
|
||||||
$avgUas = collect($data)->whereNotNull('uas')->avg('uas');
|
$avgUas = collect($data)->where('uas', '!=', null)->avg('uas');
|
||||||
$avgRata = collect($data)->whereNotNull('rata')->avg('rata');
|
$avgRata = collect($data)->where('rata', '!=', null)->avg('rata');
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="summary-grid">
|
<div class="summary-grid">
|
||||||
<div class="sum-card blue">
|
<div class="sum-card blue">
|
||||||
<div class="sum-icon">
|
<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>
|
||||||
<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-label">Avg. Tugas</div>
|
||||||
<div class="sum-value">{{ $avgTugas ? number_format($avgTugas,1) : '—' }}</div>
|
<div class="sum-value">{{ $avgTugas ? number_format($avgTugas,1) : '—' }}</div>
|
||||||
<div class="sum-sub">rata-rata nilai tugas</div>
|
<div class="sum-sub">rata-rata nilai tugas</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sum-card amber">
|
<div class="sum-card amber">
|
||||||
<div class="sum-icon">
|
<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>
|
||||||
<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-label">Avg. UTS</div>
|
||||||
<div class="sum-value">{{ $avgUts ? number_format($avgUts,1) : '—' }}</div>
|
<div class="sum-value">{{ $avgUts ? number_format($avgUts,1) : '—' }}</div>
|
||||||
<div class="sum-sub">rata-rata nilai UTS</div>
|
<div class="sum-sub">rata-rata nilai UTS</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sum-card green">
|
<div class="sum-card green">
|
||||||
<div class="sum-icon">
|
<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>
|
||||||
<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-label">Avg. UAS</div>
|
||||||
<div class="sum-value">{{ $avgUas ? number_format($avgUas,1) : '—' }}</div>
|
<div class="sum-value">{{ $avgUas ? number_format($avgUas,1) : '—' }}</div>
|
||||||
<div class="sum-sub">rata-rata nilai UAS</div>
|
<div class="sum-sub">rata-rata nilai UAS</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sum-card violet">
|
<div class="sum-card violet">
|
||||||
<div class="sum-icon">
|
<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>
|
||||||
<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-label">Avg. Keseluruhan</div>
|
||||||
<div class="sum-value">{{ $avgRata ? number_format($avgRata,1) : '—' }}</div>
|
<div class="sum-value">{{ $avgRata ? number_format($avgRata,1) : '—' }}</div>
|
||||||
<div class="sum-sub">rata-rata semua mapel</div>
|
<div class="sum-sub">rata-rata semua mapel</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- ── TABLE ──────────────────────────────────────── --}}
|
{{-- TABLE --}}
|
||||||
<div class="table-card">
|
<div class="table-card">
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-title">
|
<div class="toolbar-title">
|
||||||
<div class="toolbar-icon">
|
<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>
|
||||||
<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
|
Rekap Nilai Per Mapel
|
||||||
<span class="toolbar-badge">{{ count($data) }} mapel</span>
|
<span class="toolbar-badge">{{ count($data) }} mapel</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -507,48 +201,54 @@
|
||||||
@forelse($data as $d)
|
@forelse($data as $d)
|
||||||
@php
|
@php
|
||||||
$rata = $d['rata'] ?? null;
|
$rata = $d['rata'] ?? null;
|
||||||
$rataClass = !$rata ? 'rata-b'
|
$rataClass = !$rata ? 'null'
|
||||||
: ($rata >= 85 ? 'rata-a'
|
: ($rata >= 85 ? 'rata-a'
|
||||||
: ($rata >= 70 ? 'rata-b'
|
: ($rata >= 70 ? 'rata-b'
|
||||||
: ($rata >= 55 ? 'rata-c' : 'rata-d')));
|
: ($rata >= 55 ? 'rata-c' : 'rata-d')));
|
||||||
|
|
||||||
$barColor = !$rata ? '#2563eb'
|
$barColor = !$rata ? '#2563eb'
|
||||||
: ($rata >= 85 ? '#059669'
|
: ($rata >= 85 ? '#059669'
|
||||||
: ($rata >= 70 ? '#2563eb'
|
: ($rata >= 70 ? '#2563eb'
|
||||||
: ($rata >= 55 ? '#d97706' : '#dc2626')));
|
: ($rata >= 55 ? '#d97706' : '#dc2626')));
|
||||||
|
|
||||||
$pct = $rata ? min($rata, 100) : 0;
|
$pct = $rata ? min($rata, 100) : 0;
|
||||||
@endphp
|
@endphp
|
||||||
<tr>
|
<tr>
|
||||||
<td class="cell-no">{{ $loop->iteration }}</td>
|
<td style="font-family:'JetBrains Mono',monospace;font-size:12.5px;color:var(--text-muted);">{{ $loop->iteration }}</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<div class="mapel-cell">
|
<div class="mapel-cell">
|
||||||
<div class="mapel-dot" style="background:{{ $barColor }};"></div>
|
<div class="mapel-dot" style="background:{{ $barColor }};"></div>
|
||||||
<span class="mapel-name">{{ $d['mapel'] ?? '—' }}</span>
|
<span class="mapel-name">{{ $d['mapel'] ?? '—' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<span class="chip tugas">{{ $d['tugas'] ?? '—' }}</span>
|
@if(isset($d['tugas']) && $d['tugas'] !== null)
|
||||||
|
<span class="chip tugas">{{ $d['tugas'] }}</span>
|
||||||
|
@else
|
||||||
|
<span class="badge-pending">Belum</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<span class="chip uts">{{ $d['uts'] ?? '—' }}</span>
|
@if(isset($d['uts']) && $d['uts'] !== null)
|
||||||
|
<span class="chip uts">{{ $d['uts'] }}</span>
|
||||||
|
@else
|
||||||
|
<span class="badge-pending">Belum</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<span class="chip uas">{{ $d['uas'] ?? '—' }}</span>
|
@if(isset($d['uas']) && $d['uas'] !== null)
|
||||||
|
<span class="chip uas">{{ $d['uas'] }}</span>
|
||||||
|
@else
|
||||||
|
<span class="badge-pending">Belum</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<div class="rata-wrap">
|
<div class="rata-wrap">
|
||||||
<span class="chip {{ $rataClass }}">{{ $rata ?? '—' }}</span>
|
|
||||||
@if($rata)
|
@if($rata)
|
||||||
<div class="mini-bar">
|
<span class="chip {{ $rataClass }}">{{ $rata }}</span>
|
||||||
<div class="mini-bar-fill"
|
<div class="mini-bar">
|
||||||
style="width:{{ $pct }}%; background:{{ $barColor }};"></div>
|
<div class="mini-bar-fill" style="width:{{ $pct }}%;background:{{ $barColor }};"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<span class="badge-pending">Menunggu guru</span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -557,13 +257,9 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">
|
<td colspan="6">
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<div class="empty-icon">
|
<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>
|
||||||
<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-title">Belum Ada Data Nilai</div>
|
||||||
<div class="empty-desc">Nilai akan muncul setelah guru menginput data penilaian.</div>
|
<div class="empty-desc">Nilai akan muncul setelah guru mengirimkan nilai ke kamu.</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -574,12 +270,11 @@
|
||||||
|
|
||||||
@if(count($data) > 0)
|
@if(count($data) > 0)
|
||||||
<div class="table-footer">
|
<div class="table-footer">
|
||||||
<span>Menampilkan {{ count($data) }} mata pelajaran</span>
|
<span>{{ count($data) }} mata pelajaran</span>
|
||||||
<span>{{ now()->format('d F Y') }}</span>
|
<span>{{ now()->format('d F Y') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -429,7 +429,7 @@
|
||||||
<th class="center">Aksi</th>
|
<th class="center">Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<!-- <tbody>
|
||||||
@forelse($tugas as $t)
|
@forelse($tugas as $t)
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
<td><span class="judul-cell">{{ $t->judul }}</span></td>
|
||||||
|
|
@ -516,7 +516,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody> -->
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| OTP LOGIN (Langkah 1 & 2 di halaman login)
|
| OTP 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('/send-otp', [OtpController::class, 'sendOtp'])->name('send.otp');
|
||||||
|
|
@ -76,7 +74,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| FORCE PASSWORD (ganti password default setelah login pertama)
|
| FORCE PASSWORD
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
|
|
@ -90,10 +88,9 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| LUPA PASSWORD — via OTP (flow: kirim OTP → verify → reset)
|
| LUPA PASSWORD
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
// Langkah 1: Form kirim OTP lupa password
|
|
||||||
Route::get('/forgot-password', function () {
|
Route::get('/forgot-password', function () {
|
||||||
return view('auth.forgot-password');
|
return view('auth.forgot-password');
|
||||||
})->name('password.request');
|
})->name('password.request');
|
||||||
|
|
@ -101,14 +98,12 @@
|
||||||
Route::post('/forgot-password', [AuthController::class, 'sendOtp'])
|
Route::post('/forgot-password', [AuthController::class, 'sendOtp'])
|
||||||
->name('password.email');
|
->name('password.email');
|
||||||
|
|
||||||
// Langkah 2: Verify OTP
|
|
||||||
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp'])
|
Route::get('/verify-otp', [AuthController::class, 'showVerifyOtp'])
|
||||||
->name('verify.otp.form');
|
->name('verify.otp.form');
|
||||||
|
|
||||||
Route::post('/verify-otp', [AuthController::class, 'verifyOtp'])
|
Route::post('/verify-otp', [AuthController::class, 'verifyOtp'])
|
||||||
->name('verify.otp');
|
->name('verify.otp');
|
||||||
|
|
||||||
// Langkah 3: Reset password baru
|
|
||||||
Route::get('/reset-password', [AuthController::class, 'showResetPassword'])
|
Route::get('/reset-password', [AuthController::class, 'showResetPassword'])
|
||||||
->name('reset.password.form');
|
->name('reset.password.form');
|
||||||
|
|
||||||
|
|
@ -145,7 +140,7 @@
|
||||||
Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif'])->name('siswa.bulkNonaktif');
|
Route::post('/siswa/bulk/nonaktif', [SiswaController::class, 'bulkNonaktif'])->name('siswa.bulkNonaktif');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Siswa nonaktif/aktifkan (di luar middleware force.password agar fleksibel)
|
// Siswa nonaktif/aktifkan
|
||||||
Route::prefix('admin')->name('admin.')->group(function () {
|
Route::prefix('admin')->name('admin.')->group(function () {
|
||||||
Route::post('/siswa/nonaktif/{id}', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
|
Route::post('/siswa/nonaktif/{id}', [SiswaController::class, 'nonaktif'])->name('siswa.nonaktif');
|
||||||
Route::get('/siswa/aktifkan/{id}', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
|
Route::get('/siswa/aktifkan/{id}', [SiswaController::class, 'aktifkan'])->name('siswa.aktifkan');
|
||||||
|
|
@ -172,12 +167,19 @@
|
||||||
Route::put('/absensi/update/{id}', [AbsensiController::class, 'update'])->name('absensi.update');
|
Route::put('/absensi/update/{id}', [AbsensiController::class, 'update'])->name('absensi.update');
|
||||||
|
|
||||||
// NILAI
|
// NILAI
|
||||||
|
// CATATAN: route dengan path statis (tanpa parameter) didaftarkan lebih dulu
|
||||||
|
// agar Laravel tidak salah parsing saat ada route dengan {id}
|
||||||
Route::get('/nilai', [GuruNilai::class, 'index'])->name('nilai.index');
|
Route::get('/nilai', [GuruNilai::class, 'index'])->name('nilai.index');
|
||||||
Route::get('/nilai/input', [GuruNilai::class, 'inputNilai'])->name('nilai.input');
|
Route::get('/nilai/input', [GuruNilai::class, 'inputNilai'])->name('nilai.input');
|
||||||
Route::get('/get-siswa/{id}', [GuruNilai::class, 'getSiswaByKelas'])->name('get.siswa');
|
Route::get('/get-siswa/{id}', [GuruNilai::class, 'getSiswaByKelas'])->name('get.siswa');
|
||||||
Route::post('/nilai/store-batch', [GuruNilai::class, 'storeBatch'])->name('nilai.storeBatch');
|
Route::post('/nilai/store-batch', [GuruNilai::class, 'storeBatch'])->name('nilai.storeBatch');
|
||||||
|
Route::post('/nilai/masukkan-tugas', [GuruNilai::class, 'masukkanNilaiTugas'])->name('nilai.masukkanTugas');
|
||||||
|
Route::post('/nilai/masukkan-ujian', [GuruNilai::class, 'masukkanNilaiUjian'])->name('nilai.masukkanUjian');
|
||||||
|
Route::post('/nilai/hitung-rata', [GuruNilai::class, 'hitungRata'])->name('nilai.hitungRata');
|
||||||
|
Route::post('/nilai/kirim-ke-siswa', [GuruNilai::class, 'kirimKeSiswa'])->name('nilai.kirimKeSiswa');
|
||||||
Route::get('/nilai/{id}/edit', [GuruNilai::class, 'edit'])->name('nilai.edit');
|
Route::get('/nilai/{id}/edit', [GuruNilai::class, 'edit'])->name('nilai.edit');
|
||||||
Route::put('/nilai/{id}', [GuruNilai::class, 'update'])->name('nilai.update');
|
Route::put('/nilai/{id}', [GuruNilai::class, 'update'])->name('nilai.update');
|
||||||
|
Route::delete('/nilai/{id}', [GuruNilai::class, 'destroy'])->name('nilai.destroy');
|
||||||
|
|
||||||
// TUGAS
|
// TUGAS
|
||||||
Route::resource('tugas', TugasController::class);
|
Route::resource('tugas', TugasController::class);
|
||||||
|
|
@ -197,27 +199,9 @@
|
||||||
Route::delete('/ujian/{id}', [GuruUjian::class, 'destroy'])->name('ujian.destroy');
|
Route::delete('/ujian/{id}', [GuruUjian::class, 'destroy'])->name('ujian.destroy');
|
||||||
Route::get('/ujian/{id}/soal', [GuruUjian::class, 'soal'])->name('ujian.soal');
|
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}/soal', [GuruUjian::class, 'storeSoal'])->name('ujian.storeSoal');
|
||||||
|
Route::post('/ujian/{id}/kirim', [GuruUjian::class, 'kirim'])->name('ujian.kirim');
|
||||||
});
|
});
|
||||||
|
|
||||||
// routes/web.php — tambahkan di dalam group guru
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
| SISWA
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue