637 lines
21 KiB
PHP
637 lines
21 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Capaian;
|
|
use App\Models\Santri;
|
|
use App\Models\Materi;
|
|
use App\Models\Semester;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class CapaianController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of capaian
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = Capaian::with(['santri', 'materi', 'semester']);
|
|
|
|
// Filter santri
|
|
if ($request->filled('id_santri')) {
|
|
$query->bySantri($request->id_santri);
|
|
}
|
|
|
|
// Filter semester
|
|
if ($request->filled('id_semester')) {
|
|
$query->bySemester($request->id_semester);
|
|
}
|
|
|
|
// Filter kategori
|
|
if ($request->filled('kategori')) {
|
|
$query->byKategori($request->kategori);
|
|
}
|
|
|
|
$capaians = $query->orderBy('created_at', 'desc')
|
|
->paginate(20)
|
|
->appends(request()->query());
|
|
|
|
// Data untuk filter
|
|
$santris = Santri::aktif()->orderBy('nama_lengkap')->get();
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
return view('admin.capaian.index', compact('capaians', 'santris', 'semesters'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating new capaian
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
// Get santri list
|
|
$santris = Santri::aktif()
|
|
->select('id', 'id_santri', 'nis', 'nama_lengkap', 'kelas')
|
|
->orderBy('nama_lengkap')
|
|
->get();
|
|
|
|
// Get semester aktif
|
|
$semesterAktif = Semester::aktif()->first();
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
// Jika ada pre-selected santri
|
|
$selectedSantri = null;
|
|
$materiOptions = [];
|
|
|
|
if ($request->filled('id_santri')) {
|
|
$selectedSantri = Santri::where('id_santri', $request->id_santri)->first();
|
|
if ($selectedSantri) {
|
|
// Get materi sesuai kelas santri
|
|
$materiOptions = Materi::where('kelas', $selectedSantri->kelas)
|
|
->orderBy('kategori')
|
|
->orderBy('nama_kitab')
|
|
->get();
|
|
}
|
|
}
|
|
|
|
return view('admin.capaian.create', compact('santris', 'semesters', 'semesterAktif', 'selectedSantri', 'materiOptions'));
|
|
}
|
|
|
|
/**
|
|
* Get materi by santri kelas (AJAX)
|
|
*/
|
|
public function getMateriByKelas(Request $request)
|
|
{
|
|
$santri = Santri::where('id_santri', $request->id_santri)->first();
|
|
|
|
if (!$santri) {
|
|
return response()->json(['error' => 'Santri tidak ditemukan'], 404);
|
|
}
|
|
|
|
$materis = Materi::where('kelas', $santri->kelas)
|
|
->select('id', 'id_materi', 'kategori', 'nama_kitab', 'halaman_mulai', 'halaman_akhir', 'total_halaman')
|
|
->orderBy('kategori')
|
|
->orderBy('nama_kitab')
|
|
->get();
|
|
|
|
return response()->json([
|
|
'kelas' => $santri->kelas,
|
|
'materis' => $materis
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get detail materi (AJAX)
|
|
*/
|
|
public function getDetailMateri(Request $request)
|
|
{
|
|
$materi = Materi::where('id_materi', $request->id_materi)->first();
|
|
|
|
if (!$materi) {
|
|
return response()->json(['error' => 'Materi tidak ditemukan'], 404);
|
|
}
|
|
|
|
// Check existing capaian
|
|
$existingCapaian = null;
|
|
if ($request->filled('id_santri') && $request->filled('id_semester')) {
|
|
$existingCapaian = Capaian::where('id_santri', $request->id_santri)
|
|
->where('id_materi', $request->id_materi)
|
|
->where('id_semester', $request->id_semester)
|
|
->first();
|
|
}
|
|
|
|
return response()->json([
|
|
'materi' => $materi,
|
|
'existing_capaian' => $existingCapaian
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Store a newly created capaian
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$validated = $request->validate([
|
|
'id_santri' => 'required|exists:santris,id_santri',
|
|
'id_materi' => 'required|exists:materi,id_materi',
|
|
'id_semester' => 'required|exists:semester,id_semester',
|
|
'halaman_selesai' => 'required|string',
|
|
'catatan' => 'nullable|string',
|
|
'tanggal_input' => 'required|date',
|
|
], [
|
|
'id_santri.required' => 'Santri wajib dipilih.',
|
|
'id_materi.required' => 'Materi wajib dipilih.',
|
|
'id_semester.required' => 'Semester wajib dipilih.',
|
|
'halaman_selesai.required' => 'Halaman yang selesai wajib diisi.',
|
|
'tanggal_input.required' => 'Tanggal input wajib diisi.',
|
|
]);
|
|
|
|
// Check duplikasi
|
|
$existing = Capaian::where('id_santri', $validated['id_santri'])
|
|
->where('id_materi', $validated['id_materi'])
|
|
->where('id_semester', $validated['id_semester'])
|
|
->first();
|
|
|
|
if ($existing) {
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Capaian untuk santri, materi, dan semester ini sudah ada. Silakan edit data yang ada.');
|
|
}
|
|
|
|
Capaian::create($validated);
|
|
|
|
return redirect()->route('admin.capaian.index')
|
|
->with('success', 'Capaian berhasil ditambahkan.');
|
|
}
|
|
|
|
/**
|
|
* Display the specified capaian
|
|
*/
|
|
public function show(Capaian $capaian)
|
|
{
|
|
$capaian->load(['santri', 'materi', 'semester']);
|
|
|
|
return view('admin.capaian.show', compact('capaian'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified capaian
|
|
*/
|
|
public function edit(Capaian $capaian)
|
|
{
|
|
$capaian->load(['santri', 'materi', 'semester']);
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
return view('admin.capaian.edit', compact('capaian', 'semesters'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified capaian
|
|
*/
|
|
public function update(Request $request, Capaian $capaian)
|
|
{
|
|
$validated = $request->validate([
|
|
'halaman_selesai' => 'required|string',
|
|
'catatan' => 'nullable|string',
|
|
'tanggal_input' => 'required|date',
|
|
], [
|
|
'halaman_selesai.required' => 'Halaman yang selesai wajib diisi.',
|
|
'tanggal_input.required' => 'Tanggal input wajib diisi.',
|
|
]);
|
|
|
|
$capaian->update($validated);
|
|
|
|
return redirect()->route('admin.capaian.show', $capaian)
|
|
->with('success', 'Capaian berhasil diperbarui.');
|
|
}
|
|
|
|
/**
|
|
* Remove the specified capaian
|
|
*/
|
|
public function destroy(Capaian $capaian)
|
|
{
|
|
$santriNama = $capaian->santri->nama_lengkap;
|
|
$materiNama = $capaian->materi->nama_kitab;
|
|
|
|
$capaian->delete();
|
|
|
|
return redirect()->route('admin.capaian.index')
|
|
->with('success', "Capaian {$santriNama} untuk materi {$materiNama} berhasil dihapus.");
|
|
}
|
|
|
|
/**
|
|
* Show riwayat capaian per santri
|
|
*/
|
|
public function riwayatSantri($id_santri, Request $request)
|
|
{
|
|
$santri = Santri::where('id_santri', $id_santri)->firstOrFail();
|
|
|
|
$query = Capaian::with(['materi', 'semester'])
|
|
->bySantri($id_santri);
|
|
|
|
// Filter semester
|
|
if ($request->filled('id_semester')) {
|
|
$query->bySemester($request->id_semester);
|
|
}
|
|
|
|
$capaians = $query->orderBy('created_at', 'desc')
|
|
->paginate(15)
|
|
->appends(request()->query());
|
|
|
|
// Statistik
|
|
$totalCapaian = $capaians->total();
|
|
$rataRataPersentase = Capaian::bySantri($id_santri)->avg('persentase') ?? 0;
|
|
|
|
// Statistik per kategori
|
|
$statistikKategori = Capaian::bySantri($id_santri)
|
|
->join('materi', 'capaian.id_materi', '=', 'materi.id_materi')
|
|
->select('materi.kategori', DB::raw('AVG(capaian.persentase) as rata_rata'))
|
|
->groupBy('materi.kategori')
|
|
->get()
|
|
->pluck('rata_rata', 'kategori')
|
|
->toArray();
|
|
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
return view('admin.capaian.riwayat-santri', compact('santri', 'capaians', 'totalCapaian', 'rataRataPersentase', 'statistikKategori', 'semesters'));
|
|
}
|
|
|
|
/**
|
|
* Calculate persentase (AJAX untuk preview)
|
|
*/
|
|
public function calculatePersentase(Request $request)
|
|
{
|
|
$halamanSelesai = $request->halaman_selesai;
|
|
$idMateri = $request->id_materi;
|
|
|
|
if (empty($halamanSelesai) || empty($idMateri)) {
|
|
return response()->json(['persentase' => 0, 'jumlah' => 0]);
|
|
}
|
|
|
|
try {
|
|
$persentase = Capaian::calculatePersentase($halamanSelesai, $idMateri);
|
|
$pages = Capaian::parseHalamanSelesai($halamanSelesai);
|
|
$jumlah = count($pages);
|
|
|
|
return response()->json([
|
|
'persentase' => number_format($persentase, 2),
|
|
'jumlah' => $jumlah,
|
|
'pages' => $pages
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return response()->json(['error' => $e->getMessage()], 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dashboard capaian dengan grafik
|
|
*/
|
|
public function dashboard(Request $request)
|
|
{
|
|
// Get filter inputs
|
|
$idSantri = $request->input('id_santri');
|
|
$idSemester = $request->input('id_semester');
|
|
$kelas = $request->input('kelas');
|
|
|
|
// Get semester aktif sebagai default
|
|
$semesterAktif = Semester::aktif()->first();
|
|
$selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null);
|
|
|
|
// Data untuk filter
|
|
$santris = Santri::aktif()->orderBy('nama_lengkap')->get();
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
// Build query capaian
|
|
$query = Capaian::with(['santri', 'materi', 'semester']);
|
|
|
|
if ($idSantri) {
|
|
$query->bySantri($idSantri);
|
|
}
|
|
|
|
if ($selectedSemester) {
|
|
$query->bySemester($selectedSemester);
|
|
}
|
|
|
|
if ($kelas) {
|
|
$query->whereHas('santri', function($q) use ($kelas) {
|
|
$q->where('kelas', $kelas);
|
|
});
|
|
}
|
|
|
|
// Get data
|
|
$capaians = $query->get();
|
|
|
|
// Statistik Umum
|
|
$totalCapaian = $capaians->count();
|
|
$totalSantri = $capaians->pluck('id_santri')->unique()->count();
|
|
$rataRataPersentase = $capaians->avg('persentase') ?? 0;
|
|
$capaianSelesai = $capaians->where('persentase', '>=', 100)->count();
|
|
|
|
// Statistik per Kategori
|
|
$statistikKategori = [
|
|
'Al-Qur\'an' => [
|
|
'count' => 0,
|
|
'avg' => 0,
|
|
'selesai' => 0,
|
|
],
|
|
'Hadist' => [
|
|
'count' => 0,
|
|
'avg' => 0,
|
|
'selesai' => 0,
|
|
],
|
|
'Materi Tambahan' => [
|
|
'count' => 0,
|
|
'avg' => 0,
|
|
'selesai' => 0,
|
|
],
|
|
];
|
|
|
|
foreach ($capaians as $capaian) {
|
|
$kategori = $capaian->materi->kategori;
|
|
$statistikKategori[$kategori]['count']++;
|
|
$statistikKategori[$kategori]['avg'] += $capaian->persentase;
|
|
if ($capaian->persentase >= 100) {
|
|
$statistikKategori[$kategori]['selesai']++;
|
|
}
|
|
}
|
|
|
|
// Calculate average
|
|
foreach ($statistikKategori as $kategori => $data) {
|
|
if ($data['count'] > 0) {
|
|
$statistikKategori[$kategori]['avg'] = $data['avg'] / $data['count'];
|
|
}
|
|
}
|
|
|
|
// Data untuk grafik distribusi persentase
|
|
$distribusiPersentase = [
|
|
'0-25%' => $capaians->whereBetween('persentase', [0, 25])->count(),
|
|
'26-50%' => $capaians->whereBetween('persentase', [26, 50])->count(),
|
|
'51-75%' => $capaians->whereBetween('persentase', [51, 75])->count(),
|
|
'76-99%' => $capaians->whereBetween('persentase', [76, 99])->count(),
|
|
'100%' => $capaians->where('persentase', '>=', 100)->count(),
|
|
];
|
|
|
|
// Top 10 Santri dengan Progress Tertinggi
|
|
$topSantri = Capaian::select('id_santri', DB::raw('AVG(persentase) as rata_rata'))
|
|
->when($selectedSemester, function($q) use ($selectedSemester) {
|
|
return $q->where('id_semester', $selectedSemester);
|
|
})
|
|
->when($kelas, function($q) use ($kelas) {
|
|
return $q->whereHas('santri', function($query) use ($kelas) {
|
|
$query->where('kelas', $kelas);
|
|
});
|
|
})
|
|
->groupBy('id_santri')
|
|
->orderBy('rata_rata', 'desc')
|
|
->limit(10)
|
|
->with('santri')
|
|
->get();
|
|
|
|
// Materi dengan Progress Terendah
|
|
$materiTerendah = Capaian::select('id_materi', DB::raw('AVG(persentase) as rata_rata'), DB::raw('COUNT(*) as jumlah_santri'))
|
|
->when($selectedSemester, function($q) use ($selectedSemester) {
|
|
return $q->where('id_semester', $selectedSemester);
|
|
})
|
|
->groupBy('id_materi')
|
|
->having('rata_rata', '<', 50)
|
|
->orderBy('rata_rata', 'asc')
|
|
->limit(5)
|
|
->with('materi')
|
|
->get();
|
|
|
|
return view('admin.capaian.dashboard', compact(
|
|
'santris',
|
|
'semesters',
|
|
'semesterAktif',
|
|
'selectedSemester',
|
|
'idSantri',
|
|
'kelas',
|
|
'totalCapaian',
|
|
'totalSantri',
|
|
'rataRataPersentase',
|
|
'capaianSelesai',
|
|
'statistikKategori',
|
|
'distribusiPersentase',
|
|
'topSantri',
|
|
'materiTerendah'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Rekap capaian per kelas
|
|
*/
|
|
public function rekapKelas(Request $request)
|
|
{
|
|
$kelas = $request->input('kelas', 'Lambatan');
|
|
$idSemester = $request->input('id_semester');
|
|
|
|
$semesterAktif = Semester::aktif()->first();
|
|
$selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null);
|
|
|
|
// Get santri per kelas
|
|
$santris = Santri::where('kelas', $kelas)
|
|
->where('status', 'Aktif')
|
|
->orderBy('nama_lengkap')
|
|
->get();
|
|
|
|
// Get capaian per santri
|
|
$rekapData = [];
|
|
foreach ($santris as $santri) {
|
|
$capaians = Capaian::where('id_santri', $santri->id_santri)
|
|
->when($selectedSemester, function($q) use ($selectedSemester) {
|
|
return $q->where('id_semester', $selectedSemester);
|
|
})
|
|
->with('materi')
|
|
->get();
|
|
|
|
$rataRata = $capaians->avg('persentase') ?? 0;
|
|
$totalMateri = $capaians->count();
|
|
$selesai = $capaians->where('persentase', '>=', 100)->count();
|
|
|
|
// Per kategori
|
|
$alquran = $capaians->filter(function($c) {
|
|
return $c->materi->kategori == 'Al-Qur\'an';
|
|
})->avg('persentase') ?? 0;
|
|
|
|
$hadist = $capaians->filter(function($c) {
|
|
return $c->materi->kategori == 'Hadist';
|
|
})->avg('persentase') ?? 0;
|
|
|
|
$tambahan = $capaians->filter(function($c) {
|
|
return $c->materi->kategori == 'Materi Tambahan';
|
|
})->avg('persentase') ?? 0;
|
|
|
|
$rekapData[] = [
|
|
'santri' => $santri,
|
|
'rata_rata' => $rataRata,
|
|
'total_materi' => $totalMateri,
|
|
'selesai' => $selesai,
|
|
'alquran' => $alquran,
|
|
'hadist' => $hadist,
|
|
'tambahan' => $tambahan,
|
|
];
|
|
}
|
|
|
|
// Sort by rata-rata desc
|
|
usort($rekapData, function($a, $b) {
|
|
return $b['rata_rata'] <=> $a['rata_rata'];
|
|
});
|
|
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
return view('admin.capaian.rekap-kelas', compact('rekapData', 'kelas', 'semesters', 'selectedSemester'));
|
|
}
|
|
|
|
/**
|
|
* Detail capaian per materi (semua santri)
|
|
*/
|
|
public function detailMateri($id_materi, Request $request)
|
|
{
|
|
$materi = Materi::where('id_materi', $id_materi)->firstOrFail();
|
|
|
|
$idSemester = $request->input('id_semester');
|
|
$semesterAktif = Semester::aktif()->first();
|
|
$selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null);
|
|
|
|
// Get all capaian untuk materi ini
|
|
$capaians = Capaian::where('id_materi', $id_materi)
|
|
->when($selectedSemester, function($q) use ($selectedSemester) {
|
|
return $q->where('id_semester', $selectedSemester);
|
|
})
|
|
->with(['santri', 'semester'])
|
|
->orderBy('persentase', 'desc')
|
|
->get();
|
|
|
|
// Statistik
|
|
$totalSantri = $capaians->count();
|
|
$rataRataPersentase = $capaians->avg('persentase') ?? 0;
|
|
$santriSelesai = $capaians->where('persentase', '>=', 100)->count();
|
|
$santriMulai = $capaians->where('persentase', '>', 0)->where('persentase', '<', 100)->count();
|
|
|
|
// Distribusi persentase
|
|
$distribusi = [
|
|
'0-25%' => $capaians->whereBetween('persentase', [0, 25])->count(),
|
|
'26-50%' => $capaians->whereBetween('persentase', [26, 50])->count(),
|
|
'51-75%' => $capaians->whereBetween('persentase', [51, 75])->count(),
|
|
'76-99%' => $capaians->whereBetween('persentase', [76, 99])->count(),
|
|
'100%' => $capaians->where('persentase', '>=', 100)->count(),
|
|
];
|
|
|
|
$semesters = Semester::orderBy('tahun_ajaran', 'desc')->get();
|
|
|
|
return view('admin.capaian.detail-materi', compact(
|
|
'materi',
|
|
'capaians',
|
|
'totalSantri',
|
|
'rataRataPersentase',
|
|
'santriSelesai',
|
|
'santriMulai',
|
|
'distribusi',
|
|
'semesters',
|
|
'selectedSemester'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* API untuk data grafik (AJAX)
|
|
*/
|
|
public function apiGrafikData(Request $request)
|
|
{
|
|
$type = $request->input('type', 'kategori');
|
|
$idSemester = $request->input('id_semester');
|
|
$kelas = $request->input('kelas');
|
|
|
|
$query = Capaian::with(['santri', 'materi']);
|
|
|
|
if ($idSemester) {
|
|
$query->bySemester($idSemester);
|
|
}
|
|
|
|
if ($kelas) {
|
|
$query->whereHas('santri', function($q) use ($kelas) {
|
|
$q->where('kelas', $kelas);
|
|
});
|
|
}
|
|
|
|
$data = [];
|
|
|
|
switch ($type) {
|
|
case 'kategori':
|
|
$data = [
|
|
'labels' => ['Al-Qur\'an', 'Hadist', 'Materi Tambahan'],
|
|
'datasets' => [[
|
|
'label' => 'Rata-rata Progress (%)',
|
|
'data' => [
|
|
$query->clone()->byKategori('Al-Qur\'an')->avg('persentase') ?? 0,
|
|
$query->clone()->byKategori('Hadist')->avg('persentase') ?? 0,
|
|
$query->clone()->byKategori('Materi Tambahan')->avg('persentase') ?? 0,
|
|
],
|
|
'backgroundColor' => [
|
|
'rgba(111, 186, 157, 0.8)',
|
|
'rgba(129, 198, 232, 0.8)',
|
|
'rgba(255, 213, 107, 0.8)',
|
|
],
|
|
]]
|
|
];
|
|
break;
|
|
|
|
case 'distribusi':
|
|
$capaians = $query->get();
|
|
$data = [
|
|
'labels' => ['0-25%', '26-50%', '51-75%', '76-99%', '100%'],
|
|
'datasets' => [[
|
|
'label' => 'Jumlah Santri',
|
|
'data' => [
|
|
$capaians->whereBetween('persentase', [0, 25])->count(),
|
|
$capaians->whereBetween('persentase', [26, 50])->count(),
|
|
$capaians->whereBetween('persentase', [51, 75])->count(),
|
|
$capaians->whereBetween('persentase', [76, 99])->count(),
|
|
$capaians->where('persentase', '>=', 100)->count(),
|
|
],
|
|
'backgroundColor' => [
|
|
'rgba(255, 139, 148, 0.8)',
|
|
'rgba(255, 171, 145, 0.8)',
|
|
'rgba(255, 213, 107, 0.8)',
|
|
'rgba(129, 198, 232, 0.8)',
|
|
'rgba(111, 186, 157, 0.8)',
|
|
],
|
|
]]
|
|
];
|
|
break;
|
|
|
|
case 'trend':
|
|
// Get data per semester
|
|
$semesters = Semester::orderBy('tahun_ajaran')->orderBy('periode')->get();
|
|
$labels = [];
|
|
$dataPoints = [];
|
|
|
|
foreach ($semesters as $semester) {
|
|
$labels[] = $semester->nama_semester;
|
|
$avg = Capaian::where('id_semester', $semester->id_semester)
|
|
->when($kelas, function($q) use ($kelas) {
|
|
return $q->whereHas('santri', function($query) use ($kelas) {
|
|
$query->where('kelas', $kelas);
|
|
});
|
|
})
|
|
->avg('persentase') ?? 0;
|
|
$dataPoints[] = round($avg, 2);
|
|
}
|
|
|
|
$data = [
|
|
'labels' => $labels,
|
|
'datasets' => [[
|
|
'label' => 'Rata-rata Progress (%)',
|
|
'data' => $dataPoints,
|
|
'borderColor' => 'rgba(111, 186, 157, 1)',
|
|
'backgroundColor' => 'rgba(111, 186, 157, 0.2)',
|
|
'tension' => 0.4,
|
|
]]
|
|
];
|
|
break;
|
|
}
|
|
|
|
return response()->json($data);
|
|
}
|
|
} |