first commit

This commit is contained in:
Rifqi768 2025-06-23 09:27:48 +07:00
commit 9452027007
4926 changed files with 605002 additions and 0 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

64
.env.example Normal file
View File

@ -0,0 +1,64 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

11
.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

47
.gitignore vendored Normal file
View File

@ -0,0 +1,47 @@
<<<<<<< HEAD
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
=======
# ---> Laravel
/vendor/
node_modules/
npm-debug.log
yarn-error.log
# Laravel 4 specific
bootstrap/compiled.php
app/storage/
# Laravel 5 & Lumen specific
public/storage
public/hot
# Laravel 5 & Lumen specific with changed public path
public_html/storage
public_html/hot
storage/*.key
.env
Homestead.yaml
Homestead.json
/.vagrant
.phpunit.result.cache
>>>>>>> 296c3b905dfae6851c6e5272f852504127fe4596

110
README.md Normal file
View File

@ -0,0 +1,110 @@
<<<<<<< HEAD
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
=======
# MIF_E31221225
Sistem E-Learning ini dikembangkan sebagai solusi pembelajaran digital untuk SMAN 1 Prajekan. Aplikasi berbasis web ini dibangun menggunakan framework Laravel dan mendukung tiga jenis pengguna: admin, guru, dan siswa. Tujuan utama dari sistem ini adalah mempermudah proses pembelajaran daring maupun tatap muka yang terintegrasi dengan sistem informasi akademik sekolah.
🎯 Fitur Utama
Manajemen Pengguna
Admin dapat menambahkan, mengedit, dan menghapus akun untuk guru dan siswa. Setiap jenis pengguna memiliki hak akses yang berbeda sesuai peran.
Manajemen Materi
Guru dapat mengunggah materi pembelajaran dalam bentuk teks, dokumen, maupun video, dan memilih kelas serta tanggal terbitnya.
Tugas dan Evaluasi
Guru dapat memberikan tugas kepada siswa dengan tenggat waktu tertentu. Siswa dapat mengunggah hasil tugas, dan guru bisa memberikan nilai serta catatan evaluasi.
Kuis / Ujian Online
Mendukung pembuatan soal pilihan ganda dan esai. Siswa dapat menjawab langsung melalui sistem dan melihat hasilnya.
Visualisasi Nilai
Menyediakan grafik dan rekapitulasi nilai per siswa dan per kelas untuk memudahkan pemantauan perkembangan belajar.
Angket Pemilihan Mata Pelajaran
Siswa dapat memilih paket mata pelajaran sesuai minat dan rencana studi setelah lulus. Data ini dapat dianalisis oleh admin.
Rekap dan Cetak PDF
Nilai dan hasil evaluasi dapat diekspor dalam bentuk PDF seperti format rapor sederhana.
🧰 Teknologi yang Digunakan
Framework: Laravel 11
Database: MySQL / MariaDB
Frontend: Blade Template, Bootstrap 5
Lainnya: Chart.js untuk visualisasi nilai, DOMPDF untuk ekspor PDF
🧑‍💻 Struktur User Role
Admin: Kelola data user, tahun ajaran, angket, dan rekap nilai
Guru: Kelola materi, tugas, kuis, dan penilaian siswa
Siswa: Akses materi, kerjakan tu
>>>>>>> 296c3b905dfae6851c6e5272f852504127fe4596

View File

@ -0,0 +1,44 @@
<?php
namespace App\Exports;
use App\Models\Guru;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class GuruExport implements FromCollection, WithHeadings
{
public function collection()
{
$defaultPassword = 'passworddefault';
return Guru::select('id', 'user_id', 'nip', 'nama', 'jenis_kelamin', 'jabatan')
->with('user:id,email')
->get()
->map(function($guru) use ($defaultPassword) {
return [
'nip' => $guru->nip,
'nama' => $guru->nama,
'jenis_kelamin' => $guru->jenis_kelamin,
'jabatan' => $guru->jabatan,
'email' => $guru->user ? $guru->user->email : '',
'password' => $defaultPassword,
];
});
}
public function headings(): array
{
return [
'NIP',
'Nama',
'Jenis Kelamin',
'Jabatan',
'Email',
'Password',
];
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Exports;
use App\Models\Siswa;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class SiswaExport implements FromCollection, WithHeadings
{
public function collection()
{
return Siswa::with('kelas', 'user')->get()->map(function ($siswa) {
return [
'nisn' => $siswa->nisn,
'nama' => $siswa->nama,
'jenis_kelamin' => $siswa->jenis_kelamin,
'alamat' => $siswa->alamat,
'kelas' => $siswa->kelas->nama_kelas ?? '-',
'tingkat' => $siswa->kelas->tingkat ?? '-', // ← Tambahkan ini
'email' => $siswa->user->email ?? '-',
'password' => 'defaultpassword',
];
});
}
public function headings(): array
{
return [
'nisn',
'nama',
'jenis_kelamin',
'alamat',
'kelas',
'tingkat', // ← Tambahkan heading ini
'email',
'password'
];
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Exports;
use App\Models\SoalPilgan;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class SoalPilganExport implements FromCollection, WithHeadings
{
protected $ujian_id;
public function __construct($ujian_id)
{
$this->ujian_id = $ujian_id;
}
public function collection()
{
return SoalPilgan::where('ujian_id', $this->ujian_id)
->select('pertanyaan', 'opsi_a', 'opsi_b', 'opsi_c', 'opsi_d', 'jawaban_benar')
->get();
}
public function headings(): array
{
return [
'Pertanyaan',
'Opsi A',
'Opsi B',
'Opsi C',
'Opsi D',
'Jawaban Benar',
];
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class AdminProfileController extends Controller
{
public function edit()
{
$admin = Auth::user();
return view('admin.profile.edit', compact('admin'));
}
public function update(Request $request)
{
$admin = Auth::user();
// Validasi input
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $admin->id,
'password' => 'nullable|min:6',
]);
// Update nama dan email
$admin->name = $request->name;
$admin->email = $request->email;
// Jika password diisi, update juga
if ($request->filled('password')) {
$admin->password = Hash::make($request->password);
}
// Simpan perubahan
$admin->save();
// Redirect kembali ke halaman edit dengan notifikasi sukses
return redirect()->route('admin.profile.edit')
->with('success', 'Profil berhasil diperbarui.');
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Siswa;
use App\Models\Guru;
use App\Models\Mapel;
use App\Models\Kelas;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
public function index()
{
// Hitung total data
$jumlahSiswa = Siswa::count();
$jumlahGuru = Guru::count();
$jumlahMapel = Mapel::count();
$jumlahKelas = Kelas::count();
// Ambil user aktif gabungan guru dan siswa
$activeUsers = DB::table('users')
->where('is_active', 1)
->join('guru', 'users.id', '=', 'guru.user_id')
->select('users.email', 'guru.nama', DB::raw("'guru' as role"))
->unionAll(
DB::table('users')
->where('is_active', 1)
->join('siswa', 'users.id', '=', 'siswa.user_id')
->select('users.email', 'siswa.nama', DB::raw("'siswa' as role"))
)
->get();
return view('admin.dashboard', compact('jumlahSiswa', 'jumlahGuru', 'jumlahMapel', 'jumlahKelas', 'activeUsers'));
}
}

View File

@ -0,0 +1,151 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Guru;
use App\Models\User;
use App\Models\TahunAjaran;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\GuruImport;
use App\Exports\GuruExport;
use Illuminate\Support\Facades\Hash;
class GuruController extends Controller
{
public function index(Request $request)
{
$query = Guru::with('tahunAjaran');
if ($request->filled('search')) {
$search = $request->search;
$query->where(function ($q) use ($search) {
$q->where('nama', 'like', "%{$search}%")
->orWhere('nip', 'like', "%{$search}%")
->orWhere('jabatan', 'like', "%{$search}%");
});
}
if ($request->filled('nip')) {
$query->where('nip', 'like', "%{$request->nip}%");
}
if ($request->filled('tahun_ajaran_id')) {
$query->where('tahun_ajaran_id', $request->tahun_ajaran_id);
}
$data = $query->paginate(10);
$jabatanList = Guru::select('jabatan')->distinct()->pluck('jabatan')->toArray();
$tahunAjaranList = TahunAjaran::orderBy('tahun', 'desc')->get();
return view('admin.guru.index', compact('data', 'jabatanList', 'tahunAjaranList'));
}
public function create()
{
$tahunAjaranList = TahunAjaran::orderBy('tahun', 'desc')->get();
return view('admin.guru.create', compact('tahunAjaranList'));
}
public function store(Request $request)
{
$request->validate([
'nip' => 'required|unique:guru',
'nama' => 'required',
'email' => 'required|email|unique:users,email',
'jenis_kelamin' => 'required|in:L,P',
'jabatan' => 'required',
'tahun_ajaran_id' => 'required|exists:tahun_ajaran,id',
]);
$user = User::create([
'name' => $request->nama,
'email' => $request->email,
'password' => Hash::make('passworddefault'),
'role' => 'guru',
'is_active' => true,
]);
Guru::create([
'user_id' => $user->id,
'nip' => $request->nip,
'nama' => $request->nama,
'jenis_kelamin' => $request->jenis_kelamin,
'jabatan' => $request->jabatan,
'tahun_ajaran_id' => $request->tahun_ajaran_id,
]);
return redirect()->route('admin.guru.index')->with('success', 'Data guru berhasil ditambahkan.');
}
public function edit($id)
{
$guru = Guru::with(['user', 'tahunAjaran'])->findOrFail($id);
$tahunAjaranList = TahunAjaran::orderBy('tahun', 'desc')->get();
return view('admin.guru.edit', compact('guru', 'tahunAjaranList'));
}
public function destroy($id)
{
$guru = Guru::findOrFail($id);
// Hapus user terkait jika ada
if ($guru->user) {
$guru->user->delete();
}
// Hapus guru
$guru->delete();
return redirect()->route('admin.guru.index')->with('success', 'Data guru berhasil dihapus.');
}
public function update(Request $request, $id)
{
$guru = Guru::with('user')->findOrFail($id);
$user = $guru->user;
$request->validate([
'nip' => 'required|unique:guru,nip,' . $guru->id,
'nama' => 'required',
'email' => 'required|email|unique:users,email,' . ($user->id ?? 'null'),
'jenis_kelamin' => 'required|in:L,P',
'jabatan' => 'required',
'tahun_ajaran_id' => 'required|exists:tahun_ajaran,id',
]);
if ($user) {
$user->email = $request->email;
$user->save();
}
$guru->update([
'nip' => $request->nip,
'nama' => $request->nama,
'jenis_kelamin' => $request->jenis_kelamin,
'jabatan' => $request->jabatan,
'tahun_ajaran_id' => $request->tahun_ajaran_id,
]);
return redirect()->route('admin.guru.index')->with('success', 'Data guru berhasil diupdate.');
}
public function export()
{
return Excel::download(new GuruExport, 'data_guru_' . date('Ymd_His') . '.xlsx');
}
public function import(Request $request)
{
$request->validate([
'file' => 'required|mimes:xlsx,xls',
]);
try {
Excel::import(new GuruImport, $request->file('file'));
return back()->with('success', 'Import data guru berhasil!');
} catch (\Exception $e) {
return back()->with('error', 'Terjadi kesalahan saat mengimpor data: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Guru;
use App\Models\Mapel;
use App\Models\Kelas;
use App\Models\TahunAjaran;
use App\Models\GuruMapel;
use Illuminate\Http\Request;
use Carbon\Carbon;
class GuruMapelController extends Controller
{
public function index()
{
$data = GuruMapel::with(['guru', 'mapel', 'kelas'])->paginate(10);
return view('admin.GuruMapel.index', compact('data'));
}
public function create()
{
$guru = Guru::all();
$mapel = Mapel::all();
$kelas = Kelas::all();
return view('admin.GuruMapel.create', compact('guru', 'mapel', 'kelas'));
}
public function store(Request $request)
{
$request->validate([
'guru_id' => 'required',
'mapel_id' => 'required',
'kelas_id' => 'required',
'hari' => 'required|string|max:20',
'jam_mulai' => 'required|date_format:H:i',
'jam_selesai' => 'required|date_format:H:i|after:jam_mulai',
]);
$jamMulai = Carbon::createFromFormat('H:i', $request->jam_mulai);
$jamSelesai = Carbon::createFromFormat('H:i', $request->jam_selesai);
$jadwalBentrok = GuruMapel::where('hari', $request->hari)
->where(function ($query) use ($request) {
$query->where('guru_id', $request->guru_id)
->orWhere('kelas_id', $request->kelas_id);
})
->get();
foreach ($jadwalBentrok as $jadwal) {
$mulai = Carbon::parse($jadwal->jam_mulai);
$selesai = Carbon::parse($jadwal->jam_selesai);
if ($jamMulai->lt($selesai) && $jamSelesai->gt($mulai)) {
return redirect()->back()->withInput()->withErrors([
'jadwal' => 'Jadwal bentrok dengan jadwal guru atau kelas yang sudah ada pada hari dan jam tersebut.'
]);
}
}
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->first();
GuruMapel::create([
'guru_id' => $request->guru_id,
'mapel_id' => $request->mapel_id,
'kelas_id' => $request->kelas_id,
'hari' => $request->hari,
'jam_mulai' => $request->jam_mulai,
'jam_selesai' => $request->jam_selesai,
'tahun_ajaran_id' => $tahunAjaranAktif?->id,
]);
// ✅ Notifikasi berhasil
return redirect()->route('admin.guru-mapel.index')->with('success', 'Data berhasil ditambahkan.');
}
public function edit($id)
{
$data = GuruMapel::findOrFail($id);
$guru = Guru::all();
$mapel = Mapel::all();
$kelas = Kelas::all();
return view('admin.GuruMapel.edit', compact('data', 'guru', 'mapel', 'kelas'));
}
public function update(Request $request, $id)
{
try {
if (strlen($request->jam_mulai) > 5) {
$request->merge(['jam_mulai' => substr($request->jam_mulai, 0, 5)]);
}
if (strlen($request->jam_selesai) > 5) {
$request->merge(['jam_selesai' => substr($request->jam_selesai, 0, 5)]);
}
$request->validate([
'guru_id' => 'required',
'mapel_id' => 'required',
'kelas_id' => 'required',
'hari' => 'required|string|max:20',
'jam_mulai' => 'required|date_format:H:i',
'jam_selesai' => 'required|date_format:H:i|after:jam_mulai',
]);
$jamMulai = Carbon::createFromFormat('H:i', $request->jam_mulai);
$jamSelesai = Carbon::createFromFormat('H:i', $request->jam_selesai);
$jadwalBentrok = GuruMapel::where('hari', $request->hari)
->where(function ($query) use ($request) {
$query->where('guru_id', $request->guru_id)
->orWhere('kelas_id', $request->kelas_id);
})
->where('id', '!=', $id)
->get();
foreach ($jadwalBentrok as $jadwal) {
$mulai = Carbon::parse($jadwal->jam_mulai);
$selesai = Carbon::parse($jadwal->jam_selesai);
if ($jamMulai->lt($selesai) && $jamSelesai->gt($mulai)) {
return redirect()->back()->withInput()->withErrors([
'jadwal' => 'Jadwal bentrok dengan jadwal guru atau kelas yang sudah ada pada hari dan jam tersebut.'
]);
}
}
$item = GuruMapel::findOrFail($id);
$item->update($request->all());
// ✅ Notifikasi berhasil
return redirect('/admin/guru-mapel')->with('success', 'Data berhasil diperbarui.');
} catch (\Exception $e) {
// ❌ Notifikasi gagal
return redirect()->back()->withInput()->withErrors(['error' => $e->getMessage()]);
}
}
public function destroy($id)
{
$item = GuruMapel::findOrFail($id);
$item->delete();
// ✅ Notifikasi berhasil
return redirect('/admin/guru-mapel')->with('success', 'Data berhasil dihapus.');
}
}

View File

@ -0,0 +1,197 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Siswa;
use App\Models\PaketMapel;
use App\Models\AngketSiswa;
use App\Models\NilaiTotalMapel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class HasilAngketController extends Controller
{
public function index()
{
$siswas = Siswa::with([
'kelas',
'angket.paketPertama',
'angket.paketKedua',
'angket.paketKetiga',
'angket.paketFinal'
])->paginate(10);
return view('admin.angket.hasil.index', compact('siswas'))->with('info', 'Berikut daftar hasil angket siswa.');
}
public function edit(Siswa $siswa)
{
$paketMapels = PaketMapel::all();
$siswa->load('angket');
if (!$siswa->angket) {
$siswa->angket = new AngketSiswa([
'siswa_id' => $siswa->id,
'nama' => $siswa->nama,
'jenis_kelamin' => $siswa->jenis_kelamin,
'kelas_id' => $siswa->kelas_id
]);
}
return view('admin.angket.hasil.edit', compact('siswa', 'paketMapels'))->with('info', 'Silakan edit hasil pilihan siswa.');
}
public function update(Request $request, Siswa $siswa)
{
$request->validate([
'paket_pertama_id' => 'required|exists:paket_mapel,id',
'paket_kedua_id' => 'nullable|exists:paket_mapel,id',
'paket_ketiga_id' => 'nullable|exists:paket_mapel,id',
'cita_cita' => 'nullable|string|max:255',
'rencana_setelah_lulus' => 'nullable|string|max:255',
'paket_final_id' => 'nullable|exists:paket_mapel,id',
]);
try {
DB::beginTransaction();
$angket = AngketSiswa::updateOrCreate(
['siswa_id' => $siswa->id],
[
'nama' => $siswa->nama,
'jenis_kelamin' => $siswa->jenis_kelamin,
'kelas_id' => $siswa->kelas_id,
'paket_pertama_id' => $request->paket_pertama_id,
'paket_kedua_id' => $request->paket_kedua_id,
'paket_ketiga_id' => $request->paket_ketiga_id,
'cita_cita' => $request->cita_cita ?? '-',
'rencana_setelah_lulus' => $request->rencana_setelah_lulus ?? '-',
'paket_final_id' => $request->has('paket_final_id') ? $request->paket_final_id : $siswa->angket->paket_final_id ?? null,
]
);
DB::commit();
Log::info('Berhasil memperbarui AngketSiswa:', $angket->toArray());
return redirect()->route('admin.hasil.index')->with('success', 'Hasil pemilihan berhasil diperbarui.');
} catch (\Exception $e) {
DB::rollBack();
Log::error('Gagal memperbarui AngketSiswa: ' . $e->getMessage(), [
'siswa_id' => $siswa->id,
'request_data' => $request->all()
]);
return redirect()->back()->withInput()->withErrors('Gagal memperbarui hasil pemilihan: ' . $e->getMessage());
}
}
public function destroy(Siswa $siswa)
{
try {
AngketSiswa::where('siswa_id', $siswa->id)->delete();
return redirect()->route('admin.hasil.index')->with('success', 'Hasil pemilihan berhasil dihapus.');
} catch (\Exception $e) {
Log::error('Gagal menghapus AngketSiswa: ' . $e->getMessage(), ['siswa_id' => $siswa->id]);
return redirect()->back()->withErrors('Gagal menghapus hasil pemilihan: ' . $e->getMessage());
}
}
public function hitung(Request $request)
{
$paketMapels = PaketMapel::all();
$paketKuota = $paketMapels->pluck('kuota', 'id')->toArray();
$paketIsi = array_fill_keys(array_keys($paketKuota), []);
$ranking = NilaiTotalMapel::select('siswa_id', DB::raw('SUM(nilai_total) as total_nilai'))
->groupBy('siswa_id')
->orderByDesc('total_nilai')
->pluck('total_nilai', 'siswa_id')
->toArray();
$angketList = AngketSiswa::with('siswa')
->get()
->sortByDesc(fn($a) => $ranking[$a->siswa_id] ?? 0);
$hasilPilihanSiswa = [];
$siswaTanpaPaket = [];
foreach ($angketList as $angket) {
$siswaId = $angket->siswa_id;
$rankingSiswa = $ranking[$siswaId] ?? 0;
$pilihans = [
$angket->paket_pertama_id,
$angket->paket_kedua_id,
$angket->paket_ketiga_id,
];
$assigned = false;
foreach ($pilihans as $paketId) {
if (!$paketId || !array_key_exists($paketId, $paketKuota)) continue;
$isiSaatIni = $paketIsi[$paketId];
if (count($isiSaatIni) < $paketKuota[$paketId]) {
$paketIsi[$paketId][$siswaId] = $rankingSiswa;
$hasilPilihanSiswa[$siswaId] = $paketId;
$assigned = true;
break;
} else {
$lowest = collect($isiSaatIni)->sort()->keys()->first();
if ($rankingSiswa > $isiSaatIni[$lowest]) {
unset($paketIsi[$paketId][$lowest]);
$paketIsi[$paketId][$siswaId] = $rankingSiswa;
$hasilPilihanSiswa[$siswaId] = $paketId;
$assigned = true;
break;
}
}
}
if (!$assigned) {
$siswaTanpaPaket[] = $siswaId;
}
}
DB::beginTransaction();
try {
foreach ($angketList as $angket) {
$finalPaketId = $hasilPilihanSiswa[$angket->siswa_id] ?? null;
AngketSiswa::where('siswa_id', $angket->siswa_id)->update([
'paket_final_id' => $finalPaketId,
]);
}
DB::commit();
Log::info('Pembaruan alokasi paket final ke database berhasil.');
} catch (\Exception $e) {
DB::rollBack();
Log::error('Gagal memperbarui alokasi paket final: ' . $e->getMessage());
return redirect()->back()->withErrors('Gagal menyimpan hasil perhitungan: ' . $e->getMessage());
}
$finalAssignments = [];
foreach ($paketIsi as $paketId => $assigned) {
$finalAssignments[$paketId] = [
'paket' => $paketMapels->firstWhere('id', $paketId),
'siswas' => collect($assigned)->keys()->map(function ($siswaId) use ($angketList, $ranking) {
$angket = $angketList->firstWhere('siswa_id', $siswaId);
return [
'siswa' => $angket->siswa,
'ranking' => $ranking[$siswaId] ?? 0,
'cita_cita' => $angket->cita_cita,
'rencana_setelah_lulus' => $angket->rencana_setelah_lulus,
];
})->sortByDesc('ranking')->values()->all()
];
}
$unassignedStudents = Siswa::whereIn('id', $siswaTanpaPaket)
->with(['kelas', 'angket.paketPertama', 'angket.paketKedua', 'angket.paketKetiga'])
->get()
->sortByDesc(fn($siswa) => $ranking[$siswa->id] ?? 0);
return view('admin.angket.hasil.hitung', compact('finalAssignments', 'unassignedStudents', 'paketMapels'))
->with('success', 'Perhitungan alokasi paket mata pelajaran telah selesai.');
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\InfoAngket;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class InfoAngketController extends Controller
{
public function index()
{
$infos = InfoAngket::all();
return view('admin.angket.info.index', compact('infos'))->with('success', session('success'));
}
public function create()
{
return view('admin.angket.info.create');
}
public function store(Request $request)
{
$request->validate([
'tata_cara' => 'required',
]);
try {
InfoAngket::create($request->all());
return redirect()->route('admin.info.index')->with('success', 'Info berhasil ditambahkan');
} catch (\Exception $e) {
Log::error('Gagal menyimpan info angket: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal menyimpan info angket.');
}
}
public function edit($id)
{
$info = InfoAngket::findOrFail($id);
return view('admin.angket.info.edit', compact('info'));
}
public function update(Request $request, $id)
{
$request->validate([
'tata_cara' => 'required',
]);
try {
$info = InfoAngket::findOrFail($id);
$info->update($request->all());
return redirect()->route('admin.info.index')->with('success', 'Info berhasil diubah');
} catch (\Exception $e) {
Log::error('Gagal memperbarui info angket: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal memperbarui info angket.');
}
}
public function destroy($id)
{
try {
InfoAngket::destroy($id);
return back()->with('success', 'Info berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus info angket: ' . $e->getMessage());
return back()->withErrors('Gagal menghapus info angket.');
}
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\TahunAjaran;
use App\Models\Kelas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class KelasController extends Controller
{
public function index()
{
// Sebaiknya eager load tahunAjaran supaya tidak n+1 problem saat tampil
$data = Kelas::with('tahunAjaran')->get();
return view('admin.kelas.index', compact('data'))->with('success', session('success'));
}
public function create()
{
$tahunAjarans = TahunAjaran::all();
return view('admin.kelas.create', compact('tahunAjarans'));
}
public function store(Request $request)
{
$request->validate([
'nama_kelas' => 'required|string|max:255',
'tingkat' => 'required|string|max:50',
'jurusan' => 'nullable|string|max:100',
'tahun_ajaran_id' => 'nullable|exists:tahun_ajaran,id',
]);
try {
Kelas::create([
'nama_kelas' => $request->nama_kelas,
'tingkat' => $request->tingkat,
'jurusan' => $request->jurusan,
'tahun_ajaran_id' => $request->tahun_ajaran_id,
]);
return redirect()->route('admin.kelas.index')->with('success', 'Data kelas berhasil ditambahkan.');
} catch (\Exception $e) {
Log::error('Gagal menambahkan kelas: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal menambahkan kelas.');
}
}
public function edit($id)
{
$kelas = Kelas::findOrFail($id);
$tahunAjarans = TahunAjaran::all();
return view('admin.kelas.edit', compact('kelas', 'tahunAjarans'));
}
public function update(Request $request, $id)
{
$request->validate([
'nama_kelas' => 'required|string|max:255',
'tingkat' => 'required|string|max:50',
'jurusan' => 'nullable|string|max:100',
'tahun_ajaran_id' => 'nullable|exists:tahun_ajaran,id',
]);
try {
$kelas = Kelas::findOrFail($id);
$kelas->update([
'nama_kelas' => $request->nama_kelas,
'tingkat' => $request->tingkat,
'jurusan' => $request->jurusan,
'tahun_ajaran_id' => $request->tahun_ajaran_id,
]);
return redirect()->route('admin.kelas.index')->with('success', 'Data kelas berhasil diperbarui');
} catch (\Exception $e) {
Log::error('Gagal memperbarui kelas: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal memperbarui kelas.');
}
}
public function destroy($id)
{
try {
$kelas = Kelas::findOrFail($id);
$kelas->delete();
return redirect()->back()->with('success', 'Data kelas berhasil dihapus.');
} catch (\Exception $e) {
Log::error('Gagal menghapus kelas: ' . $e->getMessage());
return redirect()->back()->withErrors('Gagal menghapus kelas.');
}
}
public function show($id)
{
$kelas = Kelas::with(['siswa', 'tahunAjaran'])->findOrFail($id);
return view('admin.kelas.show', compact('kelas'));
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\KelasMapel;
use App\Models\Kelas;
use App\Models\Mapel;
use App\Models\TahunAjaran;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class KelasMapelController extends Controller
{
public function index(Request $request)
{
$tahunAjaranAll = TahunAjaran::orderBy('tahun', 'desc')->get();
$selectedTahun = $request->input('tahun_ajaran_id');
$query = KelasMapel::with(['kelas.tahunAjaran', 'mapel'])
->when($selectedTahun, function($q) use ($selectedTahun) {
$q->whereHas('kelas', function($q) use ($selectedTahun) {
$q->where('tahun_ajaran_id', $selectedTahun);
});
})
->orderBy('created_at', 'desc');
$data = $query->paginate(10);
return view('admin.kelas_mapel.index', compact('data', 'tahunAjaranAll', 'selectedTahun'))
->with('success', session('success'));
}
public function create()
{
$tahunAktif = TahunAjaran::where('status', 'aktif')->first();
$existing = KelasMapel::whereHas('kelas', function($q) use ($tahunAktif) {
$q->where('tahun_ajaran_id', $tahunAktif->id);
})
->select('kelas_id', 'mapel_id')
->get();
$usedCombinations = [];
foreach ($existing as $e) {
$usedCombinations[$e->kelas_id][] = $e->mapel_id;
}
return view('admin.kelas_mapel.create', [
'kelas' => Kelas::where('tahun_ajaran_id', $tahunAktif->id)->get(),
'mapel' => Mapel::all(),
'used' => $usedCombinations,
'tahunAjaran' => TahunAjaran::all(),
]);
}
public function store(Request $request)
{
$request->validate([
'kelas_id' => 'required',
'mapel_id' => 'required',
'tahun_ajaran_id' => 'required',
]);
try {
KelasMapel::create($request->all());
return redirect()->route('admin.kelas_mapel.index')->with('success', 'Data berhasil ditambahkan');
} catch (\Exception $e) {
Log::error('Gagal menambahkan data kelas mapel: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal menambahkan data.');
}
}
public function edit($id)
{
$tahunAjaranAll = TahunAjaran::all();
$data = KelasMapel::findOrFail($id);
$kelasAll = Kelas::all();
$mapelAll = Mapel::all();
return view('admin.kelas_mapel.edit', [
'data' => $data,
'kelas' => $kelasAll,
'mapel' => $mapelAll,
'tahunAjaran' => $tahunAjaranAll,
]);
}
public function update(Request $request, $id)
{
$request->validate([
'kelas_id' => 'required',
'mapel_id' => 'required',
'tahun_ajaran_id' => 'required',
]);
try {
$data = KelasMapel::findOrFail($id);
$data->update($request->all());
return redirect()->route('admin.kelas_mapel.index')->with('success', 'Data berhasil diupdate');
} catch (\Exception $e) {
Log::error('Gagal memperbarui data kelas mapel: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal memperbarui data.');
}
}
public function destroy($id)
{
try {
$data = KelasMapel::findOrFail($id);
$data->delete();
return redirect()->back()->with('success', 'Data berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus data kelas mapel: ' . $e->getMessage());
return redirect()->back()->withErrors('Gagal menghapus data.');
}
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Mapel;
use Illuminate\Support\Facades\Log;
class MapelController extends Controller
{
public function index()
{
$data = Mapel::all();
return view('admin.mapel.index', compact('data'))
->with('success', session('success'))
->withErrors(session('error'));
}
public function create()
{
return view('admin.mapel.create');
}
public function store(Request $request)
{
$request->validate([
'nama_mapel' => 'required'
]);
try {
Mapel::create($request->all());
return redirect()->route('admin.mapel.index')->with('success', 'Data berhasil ditambahkan.');
} catch (\Exception $e) {
Log::error('Gagal menambahkan mapel: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal menambahkan data.');
}
}
public function edit($id)
{
$mapel = Mapel::findOrFail($id);
return view('admin.mapel.edit', compact('mapel'));
}
public function update(Request $request, $id)
{
$request->validate([
'nama_mapel' => 'required'
]);
try {
Mapel::findOrFail($id)->update($request->all());
return redirect()->route('admin.mapel.index')->with('success', 'Data berhasil diupdate.');
} catch (\Exception $e) {
Log::error('Gagal mengupdate mapel: ' . $e->getMessage());
return back()->withInput()->withErrors('Gagal mengupdate data.');
}
}
public function destroy($id)
{
try {
Mapel::findOrFail($id)->delete();
return redirect()->route('admin.mapel.index')->with('success', 'Data berhasil dihapus.');
} catch (\Exception $e) {
Log::error('Gagal menghapus mapel: ' . $e->getMessage());
return redirect()->route('admin.mapel.index')->withErrors('Gagal menghapus data.');
}
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\PaketMapel;
use App\Models\Mapel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class PaketMapelController extends Controller
{
public function index()
{
$pakets = PaketMapel::with('mapels')->get();
return view('admin.angket.paket.index', compact('pakets'))
->with('success', session('success'))
->withErrors(session('error'));
}
public function create()
{
$mapels = Mapel::all();
return view('admin.angket.paket.create', compact('mapels'));
}
public function store(Request $request)
{
$request->validate([
'nama_paket' => 'required',
'deskripsi' => 'required',
'kuota' => 'required|integer|min:0',
'mapel_id' => 'required|array'
]);
try {
$paket = PaketMapel::create($request->only(['nama_paket', 'deskripsi', 'kuota']));
$paket->mapels()->attach($request->mapel_id);
return redirect()->route('admin.angket.paket.index')->with('success', 'Paket berhasil ditambahkan');
} catch (\Exception $e) {
Log::error('Gagal menyimpan paket: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat menambahkan paket.');
}
}
public function edit($id)
{
$paket = PaketMapel::findOrFail($id);
$mapels = Mapel::all();
return view('admin.angket.paket.edit', compact('paket', 'mapels'));
}
public function update(Request $request, $id)
{
$request->validate([
'nama_paket' => 'required',
'deskripsi' => 'required',
'kuota' => 'required|integer|min:0',
'mapel_id' => 'required|array'
]);
try {
$paket = PaketMapel::findOrFail($id);
$paket->update($request->only(['nama_paket', 'deskripsi', 'kuota']));
$paket->mapels()->sync($request->mapel_id);
return redirect()->route('admin.paket-mapel.index')->with('success', 'Paket berhasil diperbarui');
} catch (\Exception $e) {
Log::error('Gagal memperbarui paket: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat memperbarui paket.');
}
}
public function destroy($id)
{
try {
$paket = PaketMapel::findOrFail($id);
$paket->mapels()->detach();
$paket->delete();
return back()->with('success', 'Paket berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus paket: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat menghapus paket.');
}
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\ProdiLanjutan;
use App\Models\Mapel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class ProdiLanjutanController extends Controller
{
public function index()
{
$prodis = ProdiLanjutan::with('mapels')->get();
return view('admin.angket.prodi.index', compact('prodis'))
->with('success', session('success'))
->withErrors(session('error'));
}
public function create()
{
$mapels = Mapel::all();
return view('admin.angket.prodi.create', compact('mapels'));
}
public function store(Request $request)
{
$request->validate([
'nama' => 'required',
'mapel_id' => 'array|required'
]);
try {
$prodi = ProdiLanjutan::create(['nama' => $request->nama]);
$prodi->mapels()->attach($request->mapel_id);
return redirect()->route('admin.prodi-lanjutan.index')->with('success', 'Prodi berhasil ditambahkan');
} catch (\Exception $e) {
Log::error('Gagal menambahkan prodi: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat menambahkan prodi.');
}
}
public function edit($id)
{
$prodi = ProdiLanjutan::findOrFail($id);
$mapels = Mapel::all();
return view('admin.angket.prodi.edit', compact('prodi', 'mapels'));
}
public function update(Request $request, $id)
{
$request->validate([
'nama' => 'required',
'mapel_id' => 'array|required'
]);
try {
$prodi = ProdiLanjutan::findOrFail($id);
$prodi->update(['nama' => $request->nama]);
$prodi->mapels()->sync($request->mapel_id);
return redirect()->route('admin.prodi-lanjutan.index')->with('success', 'Prodi berhasil diperbarui');
} catch (\Exception $e) {
Log::error('Gagal memperbarui prodi: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat memperbarui prodi.');
}
}
public function destroy($id)
{
try {
$prodi = ProdiLanjutan::findOrFail($id);
$prodi->mapels()->detach();
$prodi->delete();
return back()->with('success', 'Prodi berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus prodi: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat menghapus prodi.');
}
}
}

View File

@ -0,0 +1,197 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Siswa;
use App\Models\Kelas;
use App\Models\User;
use App\Models\TahunAjaran;
use App\Imports\SiswaImport;
use App\Exports\SiswaExport;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
class SiswaController extends Controller
{
public function index(Request $request)
{
$kelasId = $request->kelas_id;
$tahunAjaranId = $request->tahun_ajaran_id;
$tahunAjaranList = TahunAjaran::where('status', 'aktif')->orderBy('tahun', 'desc')->get();
$query = Siswa::with(['kelas', 'tahunAjaran', 'user']);
if ($tahunAjaranId) {
$query->where('tahun_ajaran_id', $tahunAjaranId);
}
if ($kelasId) {
$query->where('kelas_id', $kelasId);
}
$data = $query->paginate(15);
$kelasList = Kelas::all();
return view('admin.siswa.index', compact('data', 'kelasList', 'tahunAjaranList'))
->with('success', session('success'))
->withErrors(session('error'));
}
public function create()
{
$kelasList = Kelas::all();
$tahunAjaranList = TahunAjaran::orderBy('tahun', 'desc')->get();
return view('admin.siswa.create', compact('kelasList', 'tahunAjaranList'));
}
public function store(Request $request)
{
try {
$tahunAjaran = TahunAjaran::where('status', 'aktif')->first();
$request->validate([
'nisn' => 'required|unique:siswa,nisn',
'nama' => 'required',
'email' => 'required|email|unique:users,email',
'kelas_id' => 'required|exists:kelas,id',
'alamat' => 'nullable',
'jenis_kelamin' => 'required|in:L,P',
]);
$user = User::create([
'name' => $request->nama,
'email' => $request->email,
'password' => Hash::make('defaultpassword'),
'role' => 'siswa',
'is_active' => 1,
]);
Siswa::create([
'user_id' => $user->id,
'nisn' => $request->nisn,
'nama' => $request->nama,
'kelas_id' => $request->kelas_id,
'alamat' => $request->alamat,
'jenis_kelamin' => $request->jenis_kelamin,
'tahun_ajaran_id' => $tahunAjaran->id ?? null,
]);
return redirect()->route('admin.siswa.index')->with('success', 'Data siswa berhasil ditambahkan');
} catch (\Exception $e) {
Log::error('Gagal menambahkan siswa: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat menambahkan siswa.');
}
}
public function edit($id)
{
$siswa = Siswa::with(['user', 'kelas', 'tahunAjaran'])->findOrFail($id);
$kelasList = Kelas::all();
$tahunAjaranList = TahunAjaran::all();
return view('admin.siswa.edit', compact('siswa', 'kelasList', 'tahunAjaranList'));
}
public function update(Request $request, $id)
{
try {
$siswa = Siswa::with('user')->findOrFail($id);
$user = $siswa->user;
$request->validate([
'nisn' => 'required|unique:siswa,nisn,' . $siswa->id,
'nama' => 'required',
'email' => 'required|email|unique:users,email,' . $user->id,
'password' => 'nullable|min:6',
'alamat' => 'nullable',
'jenis_kelamin' => 'required|in:L,P',
'kelas_id' => 'required|exists:kelas,id',
'tahun_ajaran_id' => 'required|exists:tahun_ajaran,id',
]);
$user->name = $request->nama;
$user->email = $request->email;
if ($request->filled('password')) {
$user->password = Hash::make($request->password);
}
$user->save();
$siswa->update([
'nisn' => $request->nisn,
'nama' => $request->nama,
'alamat' => $request->alamat,
'jenis_kelamin' => $request->jenis_kelamin,
'kelas_id' => $request->kelas_id,
'tahun_ajaran_id' => $request->tahun_ajaran_id,
]);
return redirect()->route('admin.siswa.index')->with('success', 'Data siswa berhasil diperbarui');
} catch (\Exception $e) {
Log::error('Gagal mengupdate siswa: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat memperbarui data siswa.');
}
}
public function destroy($id)
{
try {
$siswa = Siswa::findOrFail($id);
$user = $siswa->user;
$siswa->delete();
if ($user) {
$user->delete();
}
return back()->with('success', 'Data siswa berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus siswa: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat menghapus data siswa.');
}
}
public function import(Request $request)
{
try {
$request->validate([
'file' => 'required|mimes:xlsx,xls',
]);
Excel::import(new SiswaImport, $request->file('file'));
return back()->with('success', 'Import data siswa berhasil!');
} catch (\Exception $e) {
Log::error('Import siswa gagal: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat mengimpor data siswa.');
}
}
public function export()
{
return Excel::download(new SiswaExport, 'data_siswa.xlsx');
}
public function siswaLulus(Request $request)
{
$filterTahunAjaranId = $request->tahun_ajaran_id;
$tahunAjaranLulus = TahunAjaran::where('status', 'lulus')->pluck('id');
if ($filterTahunAjaranId) {
$tahunAjaranLulus = $tahunAjaranLulus->filter(function ($id) use ($filterTahunAjaranId) {
return $id == $filterTahunAjaranId;
});
}
$data = Siswa::with(['kelas', 'tahunAjaran', 'user'])
->whereIn('tahun_ajaran_id', $tahunAjaranLulus)
->paginate(15);
$kelasList = Kelas::all();
$tahunAjaranList = TahunAjaran::where('status', 'lulus')->get();
return view('admin.siswa.lulus', compact('data', 'kelasList', 'tahunAjaranList'));
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\TahunAjaran;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class TahunAjaranController extends Controller
{
public function index()
{
$tahunAjarans = TahunAjaran::all();
return view('admin.tahun_ajaran.index', compact('tahunAjarans'))
->with('success', session('success'))
->withErrors(session('error'));
}
public function create()
{
return view('admin.tahun_ajaran.create');
}
public function store(Request $request)
{
try {
$request->validate([
'tahun' => 'required|string|max:20',
'semester' => 'required|in:ganjil,genap',
'status' => 'required|in:aktif,tidak aktif,lulus',
]);
TahunAjaran::create($request->all());
return redirect()->route('admin.tahun_ajaran.index')
->with('success', 'Tahun Ajaran berhasil ditambahkan.');
} catch (\Exception $e) {
Log::error('Gagal menambahkan Tahun Ajaran: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat menambahkan Tahun Ajaran.');
}
}
public function edit(TahunAjaran $tahunAjaran)
{
return view('admin.tahun_ajaran.edit', compact('tahunAjaran'));
}
public function update(Request $request, TahunAjaran $tahunAjaran)
{
try {
$request->validate([
'tahun' => 'required|string|max:20',
'semester' => 'required|in:ganjil,genap',
'status' => 'required|in:aktif,tidak aktif,lulus',
]);
$tahunAjaran->update($request->all());
return redirect()->route('admin.tahun_ajaran.index')
->with('success', 'Tahun Ajaran berhasil diupdate.');
} catch (\Exception $e) {
Log::error('Gagal mengupdate Tahun Ajaran: ' . $e->getMessage());
return back()->withInput()->withErrors('Terjadi kesalahan saat mengupdate Tahun Ajaran.');
}
}
public function destroy(TahunAjaran $tahunAjaran)
{
try {
$tahunAjaran->delete();
return redirect()->route('admin.tahun_ajaran.index')
->with('success', 'Tahun Ajaran berhasil dihapus.');
} catch (\Exception $e) {
Log::error('Gagal menghapus Tahun Ajaran: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat menghapus Tahun Ajaran.');
}
}
}

View File

@ -0,0 +1,203 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
class UserManagementController extends Controller
{
const LEVEL_GURU = 'guru';
const LEVEL_SISWA = 'siswa';
public function index(Request $request)
{
try {
$levelFilter = $request->get('level');
$guruQuery = DB::table('guru')
->join('users', 'guru.user_id', '=', 'users.id')
->select(
'guru.id',
'users.email as username',
'guru.nama as nama',
DB::raw("'" . self::LEVEL_GURU . "' as level"),
'users.is_active'
);
$siswaQuery = DB::table('siswa')
->join('users', 'siswa.user_id', '=', 'users.id')
->select(
'siswa.id',
'users.email as username',
'siswa.nama as nama',
DB::raw("'" . self::LEVEL_SISWA . "' as level"),
'users.is_active'
);
$union = $guruQuery->unionAll($siswaQuery);
$users = DB::query()->fromSub($union, 'users');
if ($levelFilter) {
$users->where('level', $levelFilter);
}
$page = $request->get('page', 1);
$perPage = 10;
$allUsers = $users->get();
$currentPageItems = $allUsers->slice(($page - 1) * $perPage, $perPage)->values();
$paginatedUsers = new LengthAwarePaginator(
$currentPageItems,
$allUsers->count(),
$perPage,
$page,
['path' => $request->url(), 'query' => $request->query()]
);
$data['users'] = $paginatedUsers;
return view('admin.user.index', $data)->with('success', session('success'));
} catch (\Exception $e) {
Log::error('Gagal memuat data user: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat memuat data user.');
}
}
public function edit(Request $request, $id)
{
try {
$level = $request->level;
if ($level == self::LEVEL_GURU) {
$userDetail = DB::table('guru')->where('id', $id)->first();
} elseif ($level == self::LEVEL_SISWA) {
$userDetail = DB::table('siswa')->where('id', $id)->first();
} else {
return back()->with('error', 'Level tidak dikenali');
}
if (!$userDetail) {
return back()->with('error', 'User tidak ditemukan');
}
$user = DB::table('users')->where('id', $userDetail->user_id)->first();
if (!$user) {
return back()->with('error', 'User utama tidak ditemukan');
}
return view('admin.user.edit', [
'level' => $level,
'id' => $id,
'userDetail' => $userDetail,
'user' => $user,
]);
} catch (\Exception $e) {
Log::error('Gagal membuka halaman edit user: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat membuka halaman edit.');
}
}
public function update(Request $request, $id)
{
try {
$level = $request->level;
if ($level == self::LEVEL_GURU) {
$userDetail = DB::table('guru')->where('id', $id)->first();
} elseif ($level == self::LEVEL_SISWA) {
$userDetail = DB::table('siswa')->where('id', $id)->first();
} else {
return back()->with('error', 'Level tidak dikenali');
}
if (!$userDetail) {
return back()->with('error', 'User tidak ditemukan');
}
$userId = $userDetail->user_id;
$request->validate([
'email' => 'required|email|unique:users,email,' . $userId,
'password' => 'nullable|min:6',
]);
$dataUpdate = [
'email' => $request->email,
'updated_at' => now(),
];
if ($request->filled('password')) {
$dataUpdate['password'] = bcrypt($request->password);
}
DB::table('users')->where('id', $userId)->update($dataUpdate);
return redirect()->route('admin.users.index')->with('success', 'User berhasil diperbarui');
} catch (\Exception $e) {
Log::error('Gagal memperbarui user: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat memperbarui user.');
}
}
public function destroy(Request $request, $id)
{
try {
$level = $request->level;
if ($level == self::LEVEL_GURU) {
DB::table('guru')->where('id', $id)->delete();
} elseif ($level == self::LEVEL_SISWA) {
DB::table('siswa')->where('id', $id)->delete();
} else {
return back()->with('error', 'Level tidak dikenali');
}
return back()->with('success', 'User berhasil dihapus');
} catch (\Exception $e) {
Log::error('Gagal menghapus user: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat menghapus user.');
}
}
public function toggleAktif(Request $request, $id)
{
try {
switch ($request->level) {
case self::LEVEL_GURU:
$tbl = 'guru';
break;
case self::LEVEL_SISWA:
$tbl = 'siswa';
break;
default:
return back()->with('error', 'Level tidak valid');
}
$userDetail = DB::table($tbl)->where('id', $id)->first();
if (!$userDetail) {
return back()->with('error', 'User tidak ditemukan');
}
$user = DB::table('users')->where('id', $userDetail->user_id)->first();
if (!$user) {
return back()->with('error', 'User utama tidak ditemukan');
}
$newStatus = $user->is_active ? 0 : 1;
DB::table('users')->where('id', $userDetail->user_id)->update(['is_active' => $newStatus]);
return back()->with('success', 'Status aktifasi berhasil diubah');
} catch (\Exception $e) {
Log::error('Gagal mengubah status user: ' . $e->getMessage());
return back()->withErrors('Terjadi kesalahan saat mengubah status aktifasi.');
}
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Validation\ValidationException;
use App\Http\Requests\Auth\LoginRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*/
public function create(): View
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
]);
if (! Auth::attempt($request->only('email', 'password'), $request->boolean('remember'))) {
throw ValidationException::withMessages([//ini eror
'email' => trans('auth.failed'),
]);
}
$request->session()->regenerate();
$user = Auth::user();
if (! $user->is_active) {
Auth::logout();
return redirect('/login')->withErrors(['email' => 'Akun belum diaktifkan oleh admin.']);
}
return match ($user->role) {
'admin' => redirect('/admin/dashboard'),
'guru' => redirect('/guru/dashboard'),
'siswa' => redirect('/siswa/dashboard'),
default => abort(403),
};
}
/**
* Destroy an authenticated session.
*/
public function destroy(Request $request): RedirectResponse
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
class ConfirmablePasswordController extends Controller
{
/**
* Show the confirm password view.
*/
public function show(): View
{
return view('auth.confirm-password');
}
/**
* Confirm the user's password.
*/
public function store(Request $request): RedirectResponse
{
if (! Auth::guard('web')->validate([
'email' => $request->user()->email,
'password' => $request->password,
])) {
throw ValidationException::withMessages([
'password' => __('auth.password'),
]);
}
$request->session()->put('auth.password_confirmed_at', time());
return redirect()->intended(route('dashboard', absolute: false));
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class EmailVerificationNotificationController extends Controller
{
/**
* Send a new email verification notification.
*/
public function store(Request $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false));
}
$request->user()->sendEmailVerificationNotification();
return back()->with('status', 'verification-link-sent');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class EmailVerificationPromptController extends Controller
{
/**
* Display the email verification prompt.
*/
public function __invoke(Request $request): RedirectResponse|View
{
return $request->user()->hasVerifiedEmail()
? redirect()->intended(route('dashboard', absolute: false))
: view('auth.verify-email');
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
class NewPasswordController extends Controller
{
public function create(Request $request): View
{
return view('auth.reset-password', ['request' => $request]);
}
public function store(Request $request): RedirectResponse
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function (User $user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
if ($status == Password::PASSWORD_RESET) {
$user = User::where('email', $request->email)->first();
Auth::login($user);
$role = session('password_reset_role', $user->role);
session()->forget('password_reset_role');
return match ($role) {
'admin' => redirect()->intended('/admin/dashboard'),
'guru' => redirect()->intended('/guru/dashboard'),
'siswa' => redirect()->intended('/siswa/dashboard'),
default => redirect()->route('login'),
};
}
return back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
class PasswordController extends Controller
{
/**
* Update the user's password.
*/
public function update(Request $request): RedirectResponse
{
$validated = $request->validateWithBag('updatePassword', [
'current_password' => ['required', 'current_password'],
'password' => ['required', Password::defaults(), 'confirmed'],
]);
$request->user()->update([
'password' => Hash::make($validated['password']),
]);
return back()->with('status', 'password-updated');
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\View\View;
class PasswordResetLinkController extends Controller
{
public function create(): View
{
return view('auth.forgot-password');
}
public function store(Request $request): RedirectResponse
{
$request->validate([
'email' => ['required', 'email'],
'role' => ['required', 'in:admin,guru,siswa'],
]);
session(['password_reset_role' => $request->role]);
$status = Password::sendResetLink($request->only('email'));
return $status === Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\Guru;
use App\Models\Siswa;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
class RegisteredUserController extends Controller
{
public function create(): View
{
return view('auth.register');
}
public function store(Request $request): RedirectResponse
{
$request->validate([
'nama' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'role' => 'required|in:guru,siswa',
]);
// Simpan ke tabel users
$user = User::create([
'name' => $request->nama,
'email' => $request->email,
'password' => Hash::make($request->password),
'role' => $request->role,
'is_active' => 1,
]);
// Hubungkan ke tabel guru jika role = guru
if ($user->role == 'guru') {
$existingGuru = Guru::where('nama', $user->name)->first();
if ($existingGuru) {
// Jika guru sudah ada, hubungkan dengan user
$existingGuru->user_id = $user->id;
$existingGuru->save();
} else {
// Jika belum ada, buat baru
Guru::create([
'user_id' => $user->id,
'nama' => $user->name,
]);
}
}
// Hubungkan ke tabel siswa jika role = siswa
if ($user->role == 'siswa') {
$existingSiswa = Siswa::where('nama', $user->name)->first();
if ($existingSiswa) {
$existingSiswa->user_id = $user->id;
$existingSiswa->save();
} else {
Siswa::create([
'user_id' => $user->id,
'nama' => $user->name,
]);
}
}
Auth::login($user);
return redirect()->route('redirect-dashboard');
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\Guru;
use App\Models\GuruMapel;
use Carbon\Carbon;
class DashboardGuruController extends Controller
{
public function index()
{
$userId = auth()->id();
// Ambil data guru berdasarkan user login
$guru = Guru::where('user_id', $userId)->first();
if (!$guru) {
abort(404, 'Data guru tidak ditemukan.');
}
// Ambil semua mapel yang diampu lengkap dengan relasi kelas & mapel
$jadwal = GuruMapel::with(['mapel', 'kelas'])
->where('guru_id', $guru->id)
->get();
// Ambil daftar mapel unik
$mapelYangDiampu = $jadwal->pluck('mapel')->unique('id')->values();
// Fungsi bantu mapping nama hari ke tanggal minggu berjalan
function getDateForDay($dayName) {
$days = ['Minggu' => 0, 'Senin' => 1, 'Selasa' => 2, 'Rabu' => 3, 'Kamis' => 4, 'Jumat' => 5, 'Sabtu' => 6];
$today = Carbon::now();
$monday = $today->startOfWeek(Carbon::MONDAY); // Senin minggu ini
$dayNumber = $days[$dayName] ?? null;
if ($dayNumber === null) return null;
return $monday->copy()->addDays($dayNumber - 1)->format('Y-m-d');
}
// Buat array events untuk FullCalendar
$events = [];
foreach ($jadwal as $item) {
$tanggal = getDateForDay($item->hari);
if (!$tanggal) continue; // skip kalau hari tidak valid
// Pastikan jam mulai dan selesai ada
if (!$item->jam_mulai || !$item->jam_selesai) continue;
$events[] = [
'title' => $item->mapel->nama_mapel . ' - ' . $item->kelas->nama_kelas,
'start' => $tanggal . 'T' . $item->jam_mulai,
'end' => $tanggal . 'T' . $item->jam_selesai,
];
}
return view('guru.dashboard', compact('mapelYangDiampu', 'jadwal', 'events'));
}
}

View File

@ -0,0 +1,232 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\Siswa;
use App\Models\Kelas;
use App\Models\NilaiSikap;
use App\Models\NilaiTotalMapel;
use App\Models\Mapel;
use App\Models\Tugas;
use App\Models\Ujian;
use App\Models\PengaturanBobot;
use App\Models\TahunAjaran;
use Illuminate\Http\Request;
class EvaluasiController extends Controller
{
public function index(Request $request)
{
$guru = auth()->user()->guru;
$kelas_id = $request->kelas_id;
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->firstOrFail();
$pengaturan = PengaturanBobot::where('guru_id', $guru->id)
->where('kelas_id', $kelas_id)
->first();
$bobot_tugas = $request->bobot_tugas ?? $pengaturan->bobot_tugas ?? 20;
$bobot_ujian = $request->bobot_ujian ?? $pengaturan->bobot_ujian ?? 70;
$bobot_sikap = $request->bobot_sikap ?? $pengaturan->bobot_sikap ?? 10;
$daftarKelas = Kelas::all();
$siswaList = collect();
$tugasList = collect();
$ujianList = collect();
$mapelList = collect();
if ($kelas_id && $guru) {
$mapelList = Mapel::whereHas('guruMapel', function ($q) use ($guru, $kelas_id, $tahunAjaranAktif) {
$q->where('guru_id', $guru->id)
->where('kelas_id', $kelas_id)
->where('tahun_ajaran_id', $tahunAjaranAktif->id);
})->get();
$siswaList = Siswa::where('kelas_id', $kelas_id)
->with([
'pengumpulanTugas.tugas.materi.mapel',
'hasilUjian.ujian.mapel',
'nilaiSikap',
'nilaiTotalMapel.mapel',
])->get();
$tugasList = Tugas::whereHas('kelas', function ($q) use ($kelas_id) {
$q->where('kelas_id', $kelas_id);
})
->whereHas('materi.mapel', function ($q) use ($mapelList) {
$q->whereIn('id', $mapelList->pluck('id'));
})
->withCount('pengumpulan')
->get();
$ujianList = Ujian::whereHas('kelas', function ($q) use ($kelas_id) {
$q->where('kelas_id', $kelas_id);
})
->whereHas('mapel', function ($q) use ($mapelList) {
$q->whereIn('mapels.id', $mapelList->pluck('id'));
})
->with('hasilUjian')
->get();
foreach ($siswaList as $siswa) {
foreach ($mapelList as $mapel) {
$avg_tugas = $siswa->pengumpulanTugas
->where('tugas.materi.mapel_id', $mapel->id)
->avg('nilai') ?? 0;
$avg_ujian = $siswa->hasilUjian
->where('ujian.mapel_id', $mapel->id)
->avg('nilai_total') ?? 0;
$nilai_sikap = optional($siswa->nilaiSikap)->nilai ?? 0;
$nilai_total = round(
($avg_tugas * $bobot_tugas / 100) +
($avg_ujian * $bobot_ujian / 100) +
($nilai_sikap * $bobot_sikap / 100),
2
);
NilaiTotalMapel::updateOrCreate(
['siswa_id' => $siswa->id, 'mapel_id' => $mapel->id],
['nilai_total' => $nilai_total]
);
}
$siswa->nilai_total_per_mapel = $siswa->nilaiTotalMapel
->whereIn('mapel_id', $mapelList->pluck('id'))
->pluck('nilai_total', 'mapel_id')
->toArray();
$siswa->current_nilai_sikap = optional($siswa->nilaiSikap)->nilai;
}
}
return view('guru.evaluasi.index', compact(
'daftarKelas',
'siswaList',
'kelas_id',
'bobot_tugas',
'bobot_ujian',
'bobot_sikap',
'mapelList',
'tugasList',
'ujianList',
'tahunAjaranAktif'
));
}
public function simpanBobot(Request $request)
{
$request->validate([
'kelas_id' => 'required|exists:kelas,id',
'bobot_tugas' => 'required|numeric|min:0|max:100',
'bobot_ujian' => 'required|numeric|min:0|max:100',
'bobot_sikap' => 'required|numeric|min:0|max:100',
]);
$total = $request->bobot_tugas + $request->bobot_ujian + $request->bobot_sikap;
if ($total !== 100) {
return redirect()->back()->withErrors(['bobot' => 'Total bobot harus 100%.']);
}
$guru = auth()->user()->guru;
PengaturanBobot::updateOrCreate(
['guru_id' => $guru->id, 'kelas_id' => $request->kelas_id],
[
'bobot_tugas' => $request->bobot_tugas,
'bobot_ujian' => $request->bobot_ujian,
'bobot_sikap' => $request->bobot_sikap,
]
);
return redirect()->route('guru.evaluasi.index', ['kelas_id' => $request->kelas_id])
->with('success', 'Bobot berhasil disimpan.');
}
public function hitungNilaiTotal(Request $request)
{
$guru = auth()->user()->guru;
$kelas_id = $request->kelas_id;
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->firstOrFail();
$pengaturan = PengaturanBobot::where('guru_id', $guru->id)
->where('kelas_id', $kelas_id)
->first();
if (!$pengaturan) {
return redirect()->back()->withErrors(['msg' => 'Pengaturan bobot belum diatur untuk kelas ini.']);
}
$bobot_tugas = $pengaturan->bobot_tugas;
$bobot_ujian = $pengaturan->bobot_ujian;
$bobot_sikap = $pengaturan->bobot_sikap;
$mapelList = Mapel::whereHas('guruMapel', function ($q) use ($guru, $kelas_id, $tahunAjaranAktif) {
$q->where('guru_id', $guru->id)
->where('kelas_id', $kelas_id)
->where('tahun_ajaran_id', $tahunAjaranAktif->id);
})->get();
$siswaList = Siswa::where('kelas_id', $kelas_id)
->with([
'tugasSiswa.tugas.materi.mapel',
'hasilUjian.ujian.mapel', // ini PENTING dan WAJIB
'nilaiSikap',
'nilaiTotalMapel.mapel',
])
->get();
foreach ($siswaList as $siswa) {
foreach ($mapelList as $mapel) {
$avg_tugas = $siswa->tugasSiswa
->filter(function ($tugasSiswa) use ($mapel) {
return optional(optional($tugasSiswa->tugas)->materi)->mapel_id == $mapel->id;
})
->avg('nilai') ?? 0;
$avg_ujian = $siswa->hasilUjian
->where('ujian.mapel_id', $mapel->id)
->avg('nilai_total') ?? 0;
$nilai_sikap = optional($siswa->nilaiSikap)->nilai ?? 0;
$nilai_total = round(
($avg_tugas * $bobot_tugas / 100) +
($avg_ujian * $bobot_ujian / 100) +
($nilai_sikap * $bobot_sikap / 100),
2
);
NilaiTotalMapel::updateOrCreate(
['siswa_id' => $siswa->id, 'mapel_id' => $mapel->id],
['nilai_total' => $nilai_total]
);
}
}
return redirect()->back()->with('success', 'Nilai total berhasil dihitung ulang.');
}
public function simpanNilaiSikap(Request $request)
{
$request->validate([
'kelas_id' => 'required|exists:kelas,id',
'nilai_sikap' => 'required|array',
'nilai_sikap.*' => 'required|numeric|min:0|max:100',
]);
foreach ($request->nilai_sikap as $siswa_id => $nilai) {
NilaiSikap::updateOrCreate(
['siswa_id' => $siswa_id],
['nilai' => $nilai]
);
}
return redirect()->back()->with('success', 'Nilai sikap berhasil disimpan.');
}
}

View File

@ -0,0 +1,224 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Materi;
use App\Models\Tugas;
use App\Models\Guru;
use App\Models\Kelas;
class MateriController extends Controller
{
protected function getGuruId()
{
$guru = Guru::where('user_id', auth()->id())->firstOrFail();
return $guru->id;
}
public function index(Request $request)
{
$guruId = $this->getGuruId();
$mapelIds = Guru::find($guruId)->mapel()->pluck('mapels.id')->toArray();
$materi = Materi::with('children')
->whereNull('parent_id')
->where('guru_id', $guruId)
->whereIn('mapel_id', $mapelIds)
->get();
$mapel = Guru::find($guruId)->mapel()->first(); // ambil mapel pertama untuk passing ke view
return view('guru.materi.index', compact('materi', 'mapel'));
}
public function preview($id)
{
$materi = Materi::findOrFail($id);
$tugas = Tugas::where('materi_id', $id)->get();
return view('guru.materi.tab', compact('materi', 'tugas'));
}
public function create()
{
$guruId = auth()->user()->guru->id; // atau cara dapat guru_id sesuai setup kamu
$mapel = Guru::find($guruId)->mapel()->first();
$daftarMateriInduk = Materi::where('guru_id', $guruId)
->whereNull('parent_id')
->get();
$kelasList = Kelas::all();
return view('guru.materi.create', compact('mapel', 'daftarMateriInduk', 'kelasList'));
}
public function store(Request $request)
{
$request->validate([
'judul' => 'required',
'mapel_id' => 'required|exists:mapels,id',
'kelas_id' => 'array',
'kelas_id.*' => 'exists:kelas,id',
'tanggal_terbit' => 'array',
'tanggal_terbit.*' => 'nullable|date',
'parent_id' => 'nullable|exists:materi,id',
'is_parent' => 'required|in:0,1',
]);
$guruId = $this->getGuruId();
$materi = Materi::create([
'guru_id' => $guruId,
'mapel_id' => $request->mapel_id,
'judul' => $request->judul,
'parent_id' => $request->is_parent == 0 ? $request->parent_id : null,
'isi' => '',
]);
if ($request->has('kelas_id')) {
$kelasSyncData = [];
foreach ($request->kelas_id as $kelasId) {
$kelasSyncData[$kelasId] = [
'tanggal_terbit' => $request->tanggal_terbit[$kelasId] ?? null,
];
}
$materi->kelas()->sync($kelasSyncData);
}
return redirect()->route('guru.materi.index')
->with('success', 'Materi berhasil disimpan. Silakan isi konten materi.');
}
public function edit($id)
{
$materi = Materi::with('kelas')->findOrFail($id);
$guru = Guru::where('user_id', auth()->id())->firstOrFail();
$daftarMateriInduk = Materi::whereNull('parent_id')
->where('guru_id', $guru->id)
->where('id', '!=', $id)
->get();
$daftarMapel = $guru->mapel; // <== Tambahkan ini
$kelasList = Kelas::all();
return view('guru.materi.edit', compact('materi', 'daftarMateriInduk', 'kelasList', 'daftarMapel'));
}
public function update(Request $request, $id)
{
$materi = Materi::findOrFail($id);
$request->validate([
'judul' => 'required',
'mapel_id' => 'required|exists:mapels,id',
'kelas_id' => 'array',
'kelas_id.*' => 'exists:kelas,id',
'tanggal_terbit' => 'array',
'tanggal_terbit.*' => 'nullable|date',
'parent_id' => 'nullable|exists:materi,id',
'is_parent' => 'required|in:0,1',
]);
$materi->update([
'judul' => $request->judul,
'mapel_id' => $request->mapel_id,
'parent_id' => $request->is_parent == 0 ? $request->parent_id : null,
]);
if ($request->has('kelas_id')) {
$kelasSyncData = [];
foreach ($request->kelas_id as $kelasId) {
$kelasSyncData[$kelasId] = [
'tanggal_terbit' => $request->tanggal_terbit[$kelasId] ?? null,
];
}
$materi->kelas()->sync($kelasSyncData);
}
return redirect()->route('guru.materi.index')->with('success', 'Materi diperbarui');
}
public function editIsi($id)
{
$materi = Materi::findOrFail($id);
return view('guru.materi.edit_isi', compact('materi'));
}
public function updateIsi(Request $request, $id)
{
$materi = Materi::findOrFail($id);
$request->validate([
'isi' => 'required',
'file' => 'nullable|file|mimes:jpeg,jpg,png,gif,svg,pdf,docx,xlsx,mp4,mov|max:20480',
]);
$data = ['isi' => $request->isi];
if ($request->hasFile('file')) {
$file = $request->file('file');
$ext = $file->getClientOriginalExtension();
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'svg'])) {
$path = $file->store('materi_images', 'public');
} elseif (in_array($ext, ['pdf', 'docx', 'xlsx'])) {
$path = $file->store('materi_documents', 'public');
} elseif (in_array($ext, ['mp4', 'mov'])) {
$path = $file->store('materi_videos', 'public');
} else {
return redirect()->back()->withErrors('Format file tidak didukung');
}
$data['file'] = $path;
}
$materi->update($data);
return redirect()->route('guru.materi.index')->with('success', 'Isi materi berhasil diperbarui.');
}
public function destroy($id)
{
$materi = Materi::findOrFail($id);
$materi->delete();
return redirect()->route('guru.materi.index')->with('success', 'Materi dihapus');
}
public function upload(Request $request)
{
$request->validate([
'file' => 'required|file|mimes:jpeg,jpg,png,gif,svg,pdf,docx,xlsx,mp4|max:20480',
]);
if ($request->hasFile('file')) {
$file = $request->file('file');
$ext = $file->getClientOriginalExtension();
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'svg'])) {
$path = $file->store('materi_images', 'public');
} elseif (in_array($ext, ['pdf', 'docx', 'xlsx'])) {
$path = $file->store('materi_documents', 'public');
} elseif ($ext === 'mp4') {
$path = $file->store('materi_videos', 'public');
} else {
return response()->json(['error' => 'Format file tidak didukung'], 400);
}
return response()->json([
'location' => asset('storage/' . $path)
]);
}
return response()->json(['error' => 'Upload gagal'], 400);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\HasilUjian;
use Illuminate\Http\Request;
class NilaiEssayController extends Controller
{
public function edit($id)
{
$hasil = HasilUjian::with(['ujian', 'siswa'])->findOrFail($id);
return view('guru.ujian.nilai_essay', compact('hasil'));
}
public function update(Request $request, $id)
{
$request->validate(['nilai_essay' => 'required|integer|min:0']);
$hasil = HasilUjian::findOrFail($id);
$hasil->nilai_essay = $request->nilai_essay;
$hasil->nilai_total = $hasil->nilai_pg + $request->nilai_essay;
$hasil->save();
return redirect()->route('guru.ujian.hasil', $hasil->ujian_id)->with('success', 'Nilai essay berhasil disimpan.');
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth; // Not explicitly used but fine to keep
use App\Models\GuruMapel;
use App\Models\TahunAjaran;
use App\Models\Guru;
use App\Models\Kelas; // Tambahkan ini jika Anda punya model Kelas
class PelajaranController extends Controller
{
public function index()
{
$userId = auth()->id();
$guru = Guru::where('user_id', $userId)->firstOrFail();
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->firstOrFail();
$kelasId = request()->get('kelas_id'); // ambil filter dari form (opsional)
// Ambil semua kelas yang pernah diajar oleh guru untuk tahun ajaran aktif
// Ini sudah benar, asalkan model GuruMapel punya relasi 'kelas'
$semuaKelas = GuruMapel::where('guru_id', $guru->id)
->where('tahun_ajaran_id', $tahunAjaranAktif->id)
->with('kelas') // Memuat data kelas yang terkait langsung dengan guru_mapel
->get()
->pluck('kelas')
->unique('id')
->sortBy('nama_kelas');
$query = GuruMapel::where('guru_id', $guru->id)
->where('tahun_ajaran_id', $tahunAjaranAktif->id)
->with('mapel'); // HANYA memuat relasi 'mapel', HAPUS 'kelasMapel' yang bermasalah
// Jika Anda perlu memuat data kelas juga bersama dengan mapel,
// dan Anda tidak lagi menggunakan 'kelasMapel' sebagai pivot,
// pastikan relasi 'kelas' juga ada di model GuruMapel
// $query = GuruMapel::where('guru_id', $guru->id)
// ->where('tahun_ajaran_id', $tahunAjaranAktif->id)
// ->with(['mapel', 'kelas']); // Gunakan relasi 'kelas' langsung dari GuruMapel
if ($kelasId) {
$query->where('kelas_id', $kelasId);
}
// Pastikan Anda ingin unique per mapel_id saja, dan bukan per kombinasi mapel-kelas
$mapelYangDiampu = $query->get()->unique('mapel_id')->values();
return view('guru.pelajaran.index', compact('mapelYangDiampu', 'semuaKelas', 'kelasId'));
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;
class ProfileController extends Controller
{
public function index(): View
{
$user = Auth::user();
$guru = $user->guru;
return view('guru.profile-show', compact('user', 'guru'));
}
public function edit(): View
{
$user = Auth::user();
$guru = $user->guru;
return view('guru.profile', compact('user', 'guru'));
}
/**
* Update profil guru.
*/
public function update(Request $request)
{
$user = Auth::user();
$guru = $user->guru;
$request->validate([
'first_name' => 'required|string|max:255',
'last_name' => 'nullable|string|max:255',
'email' => 'required|email|unique:users,email,' . $user->id,
'jabatan' => 'nullable|string|max:255',
'nip' => 'nullable|string|max:255',
'jenis_kelamin' => 'nullable|in:L,P',
]);
// Update user
$fullName = trim($request->first_name . ' ' . $request->last_name);
$user->email = $request->email;
$user->name = $fullName;
$user->save();
// Update guru
$guru->nama = $fullName;
$guru->jabatan = $request->jabatan;
$guru->nip = $request->nip; // jika ingin menyimpan nip
$guru->jenis_kelamin = $request->jenis_kelamin; // jika ingin menyimpan jenis_kelamin
$guru->save();
return redirect()->route('guru.profile.index')->with('status', 'Profil berhasil diperbarui.');
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\SoalEssay;
use App\Models\Ujian;
use Illuminate\Http\Request;
class SoalEssayController extends Controller
{
public function index($ujian_id)
{
$ujian = Ujian::with('soalEssay')->findOrFail($ujian_id);
$soalEssay = SoalEssay::where('ujian_id', $ujian_id)->paginate(10); // Atau jumlah item per halaman sesuai kebutuhan
return view('guru.soal_essay.index', compact('ujian', 'soalEssay'));
}
public function create($ujian_id)
{
$ujian = Ujian::findOrFail($ujian_id);
return view('guru.soal_essay.create', compact('ujian'));
}
public function store(Request $request, $ujian_id)
{
$request->validate([
'pertanyaan' => 'required|string',
]);
SoalEssay::create([
'ujian_id' => $ujian_id,
'pertanyaan' => $request->pertanyaan,
]);
return redirect()->route('guru.ujian.soal_essay', $ujian_id)->with('success', 'Soal berhasil ditambahkan.');
}
public function edit($ujian_id, $soal_id)
{
// Mencari soal berdasarkan ID soal dan ID ujian
$soal = SoalEssay::where('ujian_id', $ujian_id)->findOrFail($soal_id);
return view('guru.soal_essay.edit', compact('soal', 'ujian_id'));
}
public function update(Request $request, $ujian_id, $soal_id)
{
$request->validate([
'pertanyaan' => 'required|string',
]);
$soal = SoalEssay::where('ujian_id', $ujian_id)->findOrFail($soal_id);
$soal->update([
'pertanyaan' => $request->pertanyaan,
]);
return redirect()->route('guru.ujian.soal_essay', $ujian_id)->with('success', 'Soal berhasil diperbarui.');
}
public function destroy($ujian_id, $soal_id)
{
// Mencari soal berdasarkan ID soal dan ID ujian
$soal = SoalEssay::where('ujian_id', $ujian_id)->findOrFail($soal_id);
// Menghapus soal
$soal->delete();
return redirect()
->route('guru.ujian.soal_essay', $ujian_id)
->with('success', 'Soal PG berhasil dihapus.');
}
}

View File

@ -0,0 +1,119 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\SoalPilgan;
use App\Models\Ujian;
use App\Exports\SoalPilganExport;
use App\Imports\SoalPilganImport;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Http\Request;
class SoalPilganController extends Controller
{
// Menampilkan daftar soal
public function index($ujian_id)
{
$ujian = Ujian::findOrFail($ujian_id);
$soalPilgan = SoalPilgan::where('ujian_id', $ujian_id)->get();
return view('guru.soal_pilgan.index', compact('ujian', 'soalPilgan'));
}
// Menampilkan form tambah soal
public function create($ujian_id)
{
$ujian = Ujian::findOrFail($ujian_id);
return view('guru.soal_pilgan.create', compact('ujian'));
}
// Menyimpan soal baru
public function store(Request $request, $ujian_id)
{
$data = $request->validate([
'pertanyaan' => 'required|string',
'opsi_a' => 'required|string',
'opsi_b' => 'required|string',
'opsi_c' => 'required|string',
'opsi_d' => 'required|string',
'jawaban_benar' => 'required|in:a,b,c,d',
]);
// Menyimpan soal
SoalPilgan::create(array_merge($data, ['ujian_id' => $ujian_id]));
return redirect()
->route('guru.ujian.soal_pg', $ujian_id)
->with('success', 'Soal PG berhasil ditambahkan.');
}
// Menampilkan form edit soal
public function edit($ujian_id, $soal_id)
{
// Mencari soal berdasarkan ID soal dan ID ujian
$soal = SoalPilgan::where('ujian_id', $ujian_id)->findOrFail($soal_id);
return view('guru.soal_pilgan.edit', compact('soal', 'ujian_id'));
}
// Memperbarui soal
public function update(Request $request, $ujian_id, $soal_id)
{
// Mencari soal berdasarkan ID soal dan ID ujian
$soal = SoalPilgan::where('ujian_id', $ujian_id)->findOrFail($soal_id);
// Validasi input
$data = $request->validate([
'pertanyaan' => 'required|string',
'opsi_a' => 'required|string',
'opsi_b' => 'required|string',
'opsi_c' => 'required|string',
'opsi_d' => 'required|string',
'jawaban_benar' => 'required|in:a,b,c,d',
]);
// Memperbarui soal
$soal->update($data);
return redirect()
->route('guru.ujian.soal_pg', $ujian_id)
->with('success', 'Soal PG berhasil diperbarui.');
}
// Menghapus soal
public function destroy($ujian_id, $soal_id)
{
// Mencari soal berdasarkan ID soal dan ID ujian
$soal = SoalPilgan::where('ujian_id', $ujian_id)->findOrFail($soal_id);
// Menghapus soal
$soal->delete();
return redirect()
->route('guru.ujian.soal_pg', $ujian_id)
->with('success', 'Soal PG berhasil dihapus.');
}
// Export Excel
public function exportExcel($ujian_id)
{
$fileName = 'soal_pilgan_ujian_'.$ujian_id.'.xlsx';
return Excel::download(new SoalPilganExport($ujian_id), $fileName);
}
// Import Excel (form upload)
public function importExcel(Request $request, $ujian_id)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,xls,csv',
]);
Excel::import(new SoalPilganImport($ujian_id), $request->file('file'));
return redirect()
->route('guru.ujian.soal_pg', $ujian_id)
->with('success', 'Soal PG berhasil diimport dari Excel.');
}
}

View File

@ -0,0 +1,329 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Tugas;
use App\Models\TugasSiswa;
use App\Models\Materi;
use App\Models\Guru;
use App\Models\Kelas;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class TugasController extends Controller
{
protected function getGuruId()
{
$guru = Guru::where('user_id', auth()->id())->firstOrFail();
return $guru->id;
}
public function index()
{
$guruId = $this->getGuruId();
// Ambil tugas yang materinya dibuat oleh guru yang sedang login
$tugasList = Tugas::with('materi', 'kelas')
->whereHas('materi', function($query) use ($guruId) {
$query->where('guru_id', $guruId);
})
->get();
return view('guru.tugas.index', compact('tugasList'));
}
public function show($id)
{
$guruId = $this->getGuruId();
$tugas = Tugas::with('materi')
->where('id', $id)
->whereHas('materi', function($query) use ($guruId) {
$query->where('guru_id', $guruId);
})
->firstOrFail();
return view('guru.tugas.show', compact('tugas'));
}
public function create()
{
$guruId = $this->getGuruId();
$materiList = Materi::where('guru_id', $guruId)->get();
$kelasList = Kelas::all();
return view('guru.tugas.create', compact('materiList', 'kelasList'));
}
public function store(Request $request)
{
$request->validate([
'materi_id' => 'required|exists:materi,id',
'judul' => 'required|string|max:255',
'deskripsi' => 'nullable|string',
'file' => 'nullable|file|mimes:pdf,docx,jpg,png,mp4,mov|max:20480',
'kelas_ids' => 'required|array|min:1',
'kelas_ids.*' => 'exists:kelas,id',
'deadlines' => 'required|array',
]);
// Cek apakah sudah ada tugas untuk materi ini
if (Tugas::where('materi_id', $request->materi_id)->exists()) {
return back()->withInput()->withErrors(['materi_id' => 'Tugas untuk materi ini sudah dibuat.']);
}
$filePath = null;
if ($request->hasFile('file')) {
$filePath = $request->file('file')->store('tugas', 'public');
}
DB::beginTransaction();
try {
$tugas = Tugas::create([
'materi_id' => $request->materi_id,
'judul' => $request->judul,
'deskripsi' => $request->deskripsi,
'file' => $filePath,
'deadline' => null,
]);
foreach ($request->kelas_ids as $kelas_id) {
$deadline = $request->deadlines[$kelas_id] ?? null;
DB::table('tugas_kelas')->insert([
'tugas_id' => $tugas->id,
'kelas_id' => $kelas_id,
'deadline' => $deadline,
'created_at' => now(),
'updated_at' => now(),
]);
}
DB::commit();
return redirect()->route('guru.tugas.index')->with('success', 'Tugas berhasil ditambahkan.');
} catch (\Exception $e) {
DB::rollBack();
return back()->withInput()->withErrors(['error' => 'Terjadi kesalahan: ' . $e->getMessage()]);
}
}
public function edit($id)
{
$guruId = $this->getGuruId();
$tugas = Tugas::with('kelas')
->where('id', $id)
->whereHas('materi', function($query) use ($guruId) {
$query->where('guru_id', $guruId);
})
->firstOrFail();
$materiList = Materi::all();
$kelasList = Kelas::all();
$tugasKelas = DB::table('tugas_kelas')->where('tugas_id', $tugas->id)->get()->keyBy('kelas_id');
return view('guru.tugas.edit', compact('tugas', 'materiList', 'kelasList', 'tugasKelas'));
}
public function update(Request $request, $id)
{
$request->validate([
'materi_id' => 'required|exists:materi,id',
'judul' => 'required|string|max:255',
'deskripsi' => 'nullable|string',
'file' => 'nullable|file|mimes:pdf,docx,jpg,png,mp4,mov|max:20480',
'kelas_ids' => 'required|array|min:1',
'kelas_ids.*' => 'exists:kelas,id',
'deadlines' => 'required|array',
]);
$tugas = Tugas::findOrFail($id);
// Cek apakah materi sudah digunakan di tugas lain
if (Tugas::where('materi_id', $request->materi_id)->where('id', '!=', $id)->exists()) {
return back()->withInput()->withErrors(['materi_id' => 'Tugas untuk materi ini sudah ada.']);
}
if ($request->hasFile('file')) {
if ($tugas->file && Storage::disk('public')->exists($tugas->file)) {
Storage::disk('public')->delete($tugas->file);
}
$filePath = $request->file('file')->store('tugas', 'public');
$tugas->file = $filePath;
}
$tugas->materi_id = $request->materi_id;
$tugas->judul = $request->judul;
$tugas->deskripsi = $request->deskripsi;
$tugas->deadline = null;
$tugas->save();
DB::beginTransaction();
try {
DB::table('tugas_kelas')->where('tugas_id', $tugas->id)->delete();
foreach ($request->kelas_ids as $kelas_id) {
$deadline = $request->deadlines[$kelas_id] ?? null;
DB::table('tugas_kelas')->insert([
'tugas_id' => $tugas->id,
'kelas_id' => $kelas_id,
'deadline' => $deadline,
'created_at' => now(),
'updated_at' => now(),
]);
}
DB::commit();
return redirect()->route('guru.tugas.index')->with('success', 'Tugas berhasil diperbarui.');
} catch (\Exception $e) {
DB::rollBack();
return back()->withInput()->withErrors(['error' => 'Terjadi kesalahan: ' . $e->getMessage()]);
}
}
public function destroy($id)
{
$guruId = $this->getGuruId();
$tugas = Tugas::where('id', $id)
->whereHas('materi', function($query) use ($guruId) {
$query->where('guru_id', $guruId);
})
->firstOrFail();
if ($tugas->file && Storage::disk('public')->exists($tugas->file)) {
Storage::disk('public')->delete($tugas->file);
}
DB::table('tugas_kelas')->where('tugas_id', $tugas->id)->delete();
$tugas->delete();
return redirect()->route('guru.tugas.index')->with('success', 'Tugas berhasil dihapus.');
}
public function daftarPenilaian(Request $request)
{
$guruId = $this->getGuruId(); // ambil id guru yang login
$kelas_id = $request->kelas_id;
// Ambil semua kelas yang pernah diberikan tugas oleh guru ini
$kelasList = DB::table('tugas_kelas')
->join('tugas', 'tugas_kelas.tugas_id', '=', 'tugas.id')
->join('kelas', 'tugas_kelas.kelas_id', '=', 'kelas.id')
->join('materi', 'tugas.materi_id', '=', 'materi.id')
->where('materi.guru_id', $guruId)
->select('kelas.id', 'kelas.nama_kelas','tingkat')
->distinct()
->get();
// Ambil tugas yang memiliki pengumpulan dan sesuai guru
$tugasQuery = Tugas::withCount(['pengumpulan'])
->with('materi')
->whereHas('materi', function ($query) use ($guruId) {
$query->where('guru_id', $guruId);
});
// Jika ada filter kelas, hanya ambil tugas yang terkait dengan kelas itu
if ($kelas_id) {
$tugasQuery->whereHas('tugasKelas', function ($query) use ($kelas_id) {
$query->where('kelas_id', $kelas_id);
});
}
$tugasList = $tugasQuery->get();
return view('guru.tugas.daftar-nilai', compact('tugasList', 'kelasList', 'kelas_id'));
}
public function penilaian(Request $request, $tugas_id)
{
$guruId = $this->getGuruId();
$kelas_id = $request->query('kelas_id'); // ambil kelas_id dari query param, bisa null
// Pastikan tugas milik guru
$tugasQuery = Tugas::with(['pengumpulan.siswa'])
->where('id', $tugas_id)
->whereHas('materi', function($q) use ($guruId) {
$q->where('guru_id', $guruId);
});
$tugas = $tugasQuery->firstOrFail();
if ($kelas_id) {
// Filter pengumpulan hanya siswa yang ada di kelas tersebut
$tugas->pengumpulan = $tugas->pengumpulan->filter(function ($pengumpulan) use ($kelas_id) {
return $pengumpulan->siswa->kelas_id == $kelas_id;
});
}
// Untuk dropdown kelas di view, ambil kelas-kelas yang terkait dengan tugas ini
$kelasList = DB::table('tugas_kelas')
->join('kelas', 'tugas_kelas.kelas_id', '=', 'kelas.id')
->where('tugas_kelas.tugas_id', $tugas->id)
->select('kelas.id', 'nama_kelas','tingkat')
->get();
return view('guru.tugas.penilaian', compact('tugas', 'kelasList', 'kelas_id'));
}
public function updateNilai(Request $request, $id)
{
$guruId = $this->getGuruId();
$tugasSiswa = TugasSiswa::findOrFail($id);
// Pastikan tugas siswa ini milik guru yang sedang login
$tugas = Tugas::where('id', $tugasSiswa->tugas_id)
->whereHas('materi', function($query) use ($guruId) {
$query->where('guru_id', $guruId);
})
->firstOrFail();
$request->validate([
'nilai' => 'nullable|numeric|min:0|max:100',
'catatan' => 'nullable|string',
]);
$tugasSiswa->nilai = $request->input('nilai');
$tugasSiswa->catatan = $request->input('catatan');
$tugasSiswa->save();
return redirect()->route('guru.tugas.penilaian', $tugasSiswa->tugas_id)
->with('success', 'Nilai berhasil diperbarui.');
}
public function grafik(Request $request)
{
$kelasId = $request->kelas_id;
if (!$kelasId) {
return redirect()->route('guru.tugas.index')->with('error', 'Silakan pilih kelas terlebih dahulu.');
}
$kelas = Kelas::findOrFail($kelasId);
$guruId = $this->getGuruId();
$data = DB::table('tugas_siswa')
->join('tugas', 'tugas_siswa.tugas_id', '=', 'tugas.id')
->join('materi', 'tugas.materi_id', '=', 'materi.id')
->join('siswa', 'tugas_siswa.siswa_id', '=', 'siswa.id')
->select('tugas.id as tugas_id', 'tugas.judul', DB::raw('AVG(nilai) as rata_rata'))
->where('siswa.kelas_id', $kelasId)
->where('materi.guru_id', $guruId)
->whereNotNull('tugas_siswa.nilai')
->groupBy('tugas.id', 'tugas.judul')
->get();
$labels = $data->pluck('judul');
$values = $data->pluck('rata_rata')->map(fn($v) => round($v, 2));
return view('guru.tugas.grafik', compact('labels', 'values', 'kelas', 'kelasId'));
}
}

View File

@ -0,0 +1,280 @@
<?php
namespace App\Http\Controllers\Guru;
use App\Http\Controllers\Controller;
use App\Models\Ujian;
use App\Models\Kelas;
use App\Models\Guru;
use App\Models\Mapel;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use App\Models\HasilUjian;
use Illuminate\Support\Facades\DB;
class UjianController extends Controller
{
protected function getGuruId()
{
$guru = Guru::where('user_id', auth()->id())->firstOrFail();
return $guru->id;
}
public function index(Request $request)
{
$guruId = $this->getGuruId(); // Mendapatkan ID guru yang login
$kelasId = $request->get('kelas_id'); // ID kelas yang difilter (jika ada)
// Ambil semua mapel yang diajar guru, bisa difilter juga berdasarkan kelas
$mapelIds = \App\Models\GuruMapel::where('guru_id', $guruId)
->when($kelasId, fn($q) => $q->where('kelas_id', $kelasId))
->pluck('mapel_id');
// Ambil ujian-ujian yang mapelnya diajar oleh guru
$ujians = \App\Models\Ujian::with('kelas', 'mapel')
->whereIn('mapel_id', $mapelIds)
->when($kelasId, function ($query) use ($kelasId) {
$query->whereHas('kelas', fn($q) => $q->where('kelas.id', $kelasId));
})
->get();
// Ambil daftar kelas untuk dropdown filter (kelas yang benar-benar diajar guru)
$daftarKelas = \App\Models\Kelas::whereIn('id', \App\Models\GuruMapel::where('guru_id', $guruId)->pluck('kelas_id'))
->get();
return view('guru.ujian.index', compact('ujians', 'daftarKelas', 'kelasId'));
}
public function show(Ujian $ujian)
{
return redirect()->route('guru.ujian.preview', $ujian->id);
}
public function detail(Ujian $ujian)
{
$ujian->load([
'soalPilgan',
'soalEssay',
'kelas' => function ($query) {
$query->withPivot('deadline', 'terbit');
}
]);
return view('guru.ujian.detail', compact('ujian'));
}
public function create()
{
$guru = auth()->user()->guru;
if (!$guru) {
return redirect()->route('guru.dashboard')->with('error', 'Data guru tidak ditemukan.');
}
$mapels = $guru->mapel;
$kelas = Kelas::all();
return view('guru.ujian.create', compact('mapels', 'kelas'));
}
public function store(Request $request)
{
$guru = auth()->user()->guru;
$request->validate([
'mapel_id' => [
'required',
'exists:mapels,id',
// Validasi custom supaya mapel yang dipilih adalah mapel yang diampu guru
function ($attribute, $value, $fail) use ($guru) {
if (!$guru->mapel->pluck('id')->contains($value)) {
$fail('Mapel yang dipilih tidak valid.');
}
},
],
'judul' => 'required',
'waktu' => 'required|numeric',
'deskripsi' => 'nullable|string',
'soal' => 'nullable|string',
'bobot_pg' => 'nullable|integer',
'bobot_essay' => 'nullable|integer',
'kelas' => 'required|array',
'kelas.*.deadline' => 'nullable|date',
'kelas.*.terbit' => 'nullable|boolean',
]);
$ujian = Ujian::create([
'mapel_id' => $request->mapel_id,
'judul' => $request->judul,
'deskripsi' => $request->deskripsi,
'tanggal_post' => now(),
'waktu' => $request->waktu,
'soal' => $request->soal ?? '',
'terbit' => false,
'bobot_pg' => $request->bobot_pg ?? 0,
'bobot_essay' => $request->bobot_essay ?? 0,
]);
$attachData = [];
foreach ($request->kelas as $kelas_id => $kelasData) {
$attachData[$kelas_id] = [
'deadline' => $kelasData['deadline'] ?? null,
'terbit' => $kelasData['terbit'] ?? false,
];
}
$ujian->kelas()->attach($attachData);
return redirect()->route('guru.ujian.index')->with('success', 'Ujian berhasil dibuat.');
}
public function edit(Ujian $ujian)
{
$ujian->load(['soalPilgan', 'soalEssay', 'kelas']);
$kelas = Kelas::all();
$kelasDeadline = $ujian->kelas->keyBy('id')->map(function ($kelas) {
return $kelas->pivot->deadline;
});
$kelasTerbit = $ujian->kelas->keyBy('id')->map(function ($kelas) {
return $kelas->pivot->terbit;
});
return view('guru.ujian.edit', compact('ujian', 'kelas', 'kelasDeadline', 'kelasTerbit'));
}
public function update(Request $request, Ujian $ujian)
{
$request->validate([
'judul' => 'required',
'waktu' => 'required|numeric',
'deskripsi' => 'nullable|string',
'soal' => 'nullable|string',
'bobot_pg' => 'nullable|integer',
'bobot_essay' => 'nullable|integer',
'kelas' => 'required|array',
'kelas.*.deadline' => 'nullable|date',
'kelas.*.terbit' => 'nullable|boolean',
]);
$ujian->update([
'judul' => $request->judul,
'deskripsi' => $request->deskripsi,
'waktu' => $request->waktu,
'soal' => $request->soal ?? '',
'bobot_pg' => $request->bobot_pg ?? 0,
'bobot_essay' => $request->bobot_essay ?? 0,
]);
$syncData = [];
foreach ($request->kelas as $kelas_id => $kelasData) {
$syncData[$kelas_id] = [
'deadline' => $kelasData['deadline'] ?? null,
'terbit' => $kelasData['terbit'] ?? false,
];
}
$ujian->kelas()->sync($syncData);
return redirect()->route('guru.ujian.index')->with('success', 'Ujian berhasil diperbarui.');
}
public function updateStatus(Ujian $ujian)
{
$ujian->terbit = !$ujian->terbit;
$ujian->save();
return redirect()->route('guru.ujian.index')->with('success', 'Status ujian berhasil diperbarui.');
}
public function destroy(Ujian $ujian)
{
$ujian->kelas()->detach();
$ujian->delete();
return back()->with('success', 'Ujian berhasil dihapus.');
}
public function preview($id)
{
$ujian = Ujian::with('kelas')->findOrFail($id);
return view('guru.ujian.preview', compact('ujian'));
}
public function daftarHasil(Request $request)
{
$guruId = $this->getGuruId(); // Ambil ID guru login
$kelasId = $request->get('kelas_id');
// Ambil mapel yang diajar guru
$mapelIds = \App\Models\GuruMapel::where('guru_id', $guruId)
->when($kelasId, fn($q) => $q->where('kelas_id', $kelasId))
->pluck('mapel_id');
// Ambil semua ujian yang punya hasil, dibuat oleh guru login
$ujians = \App\Models\Ujian::withCount('hasilUjian')
->whereIn('mapel_id', $mapelIds)
->whereHas('hasilUjian')
->when($kelasId, function ($query) use ($kelasId) {
$query->whereHas('kelas', fn($q) => $q->where('kelas.id', $kelasId));
})
->get();
// Ambil daftar kelas untuk dropdown
$daftarKelas = \App\Models\Kelas::whereIn('id', \App\Models\GuruMapel::where('guru_id', $guruId)->pluck('kelas_id'))->get();
return view('guru.ujian.daftar-hasil', compact('ujians', 'daftarKelas', 'kelasId'));
}
public function hasil(Request $request, $ujian_id)
{
$guruId = $this->getGuruId();
$kelasId = $request->get('kelas_id');
// Ambil mapel yang diajar oleh guru
$mapelIds = \App\Models\GuruMapel::where('guru_id', $guruId)->pluck('mapel_id');
// Validasi bahwa ujian benar-benar milik guru login
$ujian = \App\Models\Ujian::with(['hasilUjian.siswa', 'kelas'])
->where('id', $ujian_id)
->whereIn('mapel_id', $mapelIds)
->firstOrFail();
// Filter hasilUjian berdasarkan kelas jika diminta
if ($kelasId) {
$ujian->setRelation('hasilUjian', $ujian->hasilUjian->filter(function ($hasil) use ($kelasId) {
return $hasil->siswa->kelas_id == $kelasId;
}));
}
// Ambil daftar kelas dari relasi ujian
$daftarKelas = $ujian->kelas;
return view('guru.ujian.hasil', compact('ujian', 'daftarKelas', 'kelasId'));
}
public function grafik(Request $request)
{
$kelasId = $request->kelas_id;
if (!$kelasId) {
return redirect()->route('guru.ujian.hasil.index')->with('error', 'Silakan pilih kelas terlebih dahulu.');
}
$kelas = Kelas::find($kelasId); // ambil data kelas
$data = HasilUjian::select('ujian_id', DB::raw('AVG(nilai_total) as rata_rata'))
->whereNotNull('nilai_total')
->whereHas('siswa', fn($q) => $q->where('kelas_id', $kelasId))
->groupBy('ujian_id')
->with('ujian:id,judul')
->get();
$labels = $data->map(fn($item) => $item->ujian->judul);
$values = $data->map(fn($item) => round($item->rata_rata, 2));
return view('guru.ujian.grafik', compact('labels', 'values', 'kelasId', 'kelas'));
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;
class ProfileController extends Controller
{
/**
* Display the user's profile form.
*/
public function edit(Request $request): View
{
return view('profile.edit', [
'user' => $request->user(),
]);
}
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
}
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
/**
* Delete the user's account.
*/
public function destroy(Request $request): RedirectResponse
{
$request->validateWithBag('userDeletion', [
'password' => ['required', 'current_password'],
]);
$user = $request->user();
Auth::logout();
$user->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return Redirect::to('/');
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Siswa;
use App\Models\AngketSiswa;
use App\Models\PaketMapel;
use App\Models\ProdiLanjutan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class AngketController extends Controller
{
// Menampilkan halaman utama angket siswa
public function index()
{
// Ambil data siswa berdasarkan user yang login
$siswa = Siswa::where('user_id', Auth::id())->first();
// Ambil data angket yang sudah diisi siswa (jika ada)
$angket = AngketSiswa::where('siswa_id', $siswa->id)->first();
// Ambil semua data paket mapel beserta relasi mapel
$paketMapels = PaketMapel::with('mapels')->get();
// Hitung jumlah peminat tiap paket (manual karena tidak bisa pakai withCount)
foreach ($paketMapels as $paket) {
$paket->jumlah_peminat = AngketSiswa::where('paket_pertama_id', $paket->id)
->orWhere('paket_kedua_id', $paket->id)
->orWhere('paket_ketiga_id', $paket->id)
->count();
}
return view('siswa.angket.index', compact('siswa', 'angket', 'paketMapels'));
}
// Menampilkan halaman info angket untuk siswa
public function info()
{
// Ambil informasi angket terbaru
$info = \App\Models\InfoAngket::latest()->first();
// Ambil semua paket mapel dan prodi lanjutan untuk ditampilkan
$pakets = PaketMapel::with('mapels')->get();
$prodis = ProdiLanjutan::with('mapels')->get();
return view('siswa.angket.info', compact('info', 'pakets', 'prodis'));
}
// Menampilkan form pengisian angket
public function create()
{
// Ambil data siswa yang login
$siswa = Siswa::where('user_id', Auth::id())->first();
// Ambil data prodi lanjutan dan paket mapel
$prodis = ProdiLanjutan::with('mapels')->get();
$paketMapels = PaketMapel::with('mapels')->get();
// Cek apakah siswa sudah pernah mengisi angket
$angket = AngketSiswa::where('siswa_id', $siswa->id)->first();
return view('siswa.angket.create', compact('siswa', 'prodis', 'paketMapels', 'angket'));
}
// Menyimpan data angket yang diisi siswa
public function store(Request $request)
{
// Validasi input siswa
$request->validate([
'prodi_id' => 'required|exists:prodi_lanjutan,id',
'paket_pertama_id' => 'required|exists:paket_mapel,id',
'paket_kedua_id' => 'nullable|exists:paket_mapel,id|different:paket_pertama_id',
'paket_ketiga_id' => 'nullable|exists:paket_mapel,id|different:paket_pertama_id|different:paket_kedua_id',
'cita_cita' => 'nullable|string',
'rencana' => 'nullable|string',
]);
// Ambil data siswa yang login
$siswa = Siswa::where('user_id', Auth::id())->first();
// Cek apakah siswa sudah pernah mengisi angket (hanya sekali boleh)
$count = AngketSiswa::where('siswa_id', $siswa->id)->count();
if ($count >= 1) {
return redirect()->route('siswa.angket.index')->with('error', 'Anda sudah mengisi angket.');
}
// Simpan data angket ke database
AngketSiswa::create([
'siswa_id' => $siswa->id,
'nama' => $siswa->nama,
'jenis_kelamin' => $siswa->jenis_kelamin,
'kelas_id' => $siswa->kelas_id,
'cita_cita' => $request->cita_cita ?? '-',
'rencana_setelah_lulus' => $request->rencana ?? '-',
'prodi_id' => $request->prodi_id,
'paket_pertama_id' => $request->paket_pertama_id,
'paket_kedua_id' => $request->paket_kedua_id,
'paket_ketiga_id' => $request->paket_ketiga_id,
'paket_final_id' => null, // Finalisasi dilakukan oleh admin nanti
]);
return redirect()->route('siswa.angket.index')->with('success', 'Pilihan paket mapel berhasil disimpan.');
}
// Menampilkan hasil angket yang sudah diisi siswa
public function hasil()
{
// Ambil data siswa yang login
$siswa = Siswa::where('user_id', Auth::id())->first();
// Ambil data angket milik siswa tersebut
$angket = AngketSiswa::where('siswa_id', $siswa->id)->first();
if (!$angket) {
return redirect()->route('siswa.angket.index')->with('error', 'Belum ada data angket.');
}
// Ambil data masing-masing paket mapel yang dipilih siswa
$paket1 = PaketMapel::with('mapels')->find($angket->paket_pertama_id);
$paket2 = PaketMapel::with('mapels')->find($angket->paket_kedua_id);
$paket3 = PaketMapel::with('mapels')->find($angket->paket_ketiga_id);
$paketFinal = $angket->paket_final_id ? PaketMapel::with('mapels')->find($angket->paket_final_id) : null;
// Ambil ranking siswa berdasarkan total nilai dari tabel nilai_total_mapel
$ranking = DB::table('nilai_total_mapel')
->select('siswa_id', DB::raw('SUM(nilai_total) as total_nilai'))
->groupBy('siswa_id')
->orderByDesc('total_nilai')
->get();
// Ambil nilai total siswa
$nilaiSiswa = $ranking->firstWhere('siswa_id', $siswa->id);
// Cari posisi ranking siswa
$peringkat = $ranking->search(fn($item) => $item->siswa_id == $siswa->id);
return view('siswa.angket.hasil', compact('paket1', 'paket2', 'paket3', 'paketFinal', 'nilaiSiswa', 'peringkat'));
}
}

View File

@ -0,0 +1,116 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\Siswa;
use App\Models\GuruMapel;
use App\Models\Tugas;
use App\Models\Ujian;
use Illuminate\Database\Eloquent\Builder;
use Carbon\Carbon;
class DashboardSiswaController extends Controller
{
public function index()
{
$siswa = Siswa::where('user_id', Auth::id())->firstOrFail();
$guruMapel = GuruMapel::with('mapel')
->where('kelas_id', $siswa->kelas_id)
->get();
$hariIni = Carbon::now()->locale('id')->isoFormat('dddd');
$jadwalHariIni = $guruMapel->filter(fn($item) =>
strtolower($item->hari) === strtolower($hariIni)
);
$hariMap = [
'Senin' => 0,
'Selasa' => 1,
'Rabu' => 2,
'Kamis' => 3,
'Jumat' => 4,
'Sabtu' => 5,
'Minggu' => 6,
];
// Pastikan jadwalHariIni Collection, map hasilnya dan collect ulang supaya pasti Collection
$eventsMapel = collect($jadwalHariIni->map(function ($item) use ($hariMap) {
$hari = $hariMap[ucfirst(strtolower($item->hari))] ?? 0;
$startDate = Carbon::now()->startOfWeek()->addDays($hari)->format('Y-m-d');
return [
'title' => $item->mapel->nama_mapel ?? 'Mata Pelajaran',
'start' => $startDate . 'T' . $item->jam_mulai,
'end' => $startDate . 'T' . $item->jam_selesai,
'color' => '#6f42c1',
];
}));
$tugasHariIni = Tugas::whereHas('kelas', function ($q) use ($siswa) {
$q->where('kelas_id', $siswa->kelas_id);
})
->whereDate('deadline', Carbon::today())
->with('materi')
->get();
// Pastikan eventsTugas juga Collection
$eventsTugas = collect($tugasHariIni->map(function ($tugas) {
$start = $tugas->tanggal ?? Carbon::today()->toDateString();
$deadline = $tugas->deadline ?? $start;
return [
'title' => 'Tugas: ' . $tugas->judul,
'start' => $start . 'T00:00:00',
'end' => $deadline . 'T23:59:59',
'color' => '#ffc107',
'url' => route('siswa.tugas.show', $tugas->id),
];
}));
$ujianHariIni = Ujian::where('terbit', true)
->whereHas('kelas', function (Builder $q) use ($siswa) {
$q->where('kelas.id', $siswa->kelas_id)
->where('kelas_ujian.deadline', '>=', Carbon::today()->toDateString());
})
->with(['kelas' => function ($q) use ($siswa) {
$q->where('kelas.id', $siswa->kelas_id);
}, 'mapel'])
->get();
// Pastikan eventsUjian Collection
$eventsUjian = collect($ujianHariIni->map(function ($ujian) use ($siswa) {
$kelas = $ujian->kelas->firstWhere('id', $siswa->kelas_id);
$start = $ujian->waktu_mulai ?? $ujian->tanggal_ujian ?? Carbon::now();
$end = $ujian->waktu_selesai ?? ($kelas?->pivot?->deadline ?? $start);
return [
'title' => 'Ujian: ' . ($ujian->judul ?? 'Tanpa Judul'),
'start' => Carbon::parse($start)->format('Y-m-d\TH:i:s'),
'end' => Carbon::parse($end)->format('Y-m-d\TH:i:s'),
'color' => '#dc3545',
'url' => route('siswa.ujian.show', $ujian->id),
];
}));
// Sekarang merge aman karena semuanya Collection
$events = $eventsMapel
->merge($eventsTugas)
->merge($eventsUjian);
return view('siswa.dashboard', [
'jumlahMapel' => $guruMapel->count(),
'jumlahTugas' => $tugasHariIni->count(),
'jumlahUjian' => $ujianHariIni->count(),
'guruMapel' => $guruMapel,
'events' => $events,
'tugasHariIni' => $tugasHariIni,
'ujianHariIni' => $ujianHariIni,
'jadwalHariIni' => $jadwalHariIni,
]);
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace App\Http\Controllers\Siswa;
use PDF;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\HasilUjian;
use App\Models\TugasSiswa;
use App\Models\TahunAjaran;
use App\Models\NilaiTotalMapel;
class EvaluasiSiswaController extends Controller
{
public function mapel()
{
$siswa = Auth::user()->siswa()->with('kelas.mapel')->first();
if (!$siswa || !$siswa->kelas) {
abort(404, 'Data kelas siswa tidak ditemukan.');
}
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->firstOrFail();
$mapelList = $siswa->kelas->mapel;
$nilaiTugas = TugasSiswa::where('siswa_id', $siswa->id)
->whereNotNull('nilai')
->with('tugas.materi')
->get();
$nilaiUjian = HasilUjian::where('siswa_id', $siswa->id)
->whereNotNull('nilai_total')
->with('ujian')
->get();
$nilaiTotalMapel = NilaiTotalMapel::where('siswa_id', $siswa->id)
->whereIn('mapel_id', $mapelList->pluck('id'))
->pluck('nilai_total', 'mapel_id')
->toArray();
$rataNilai = [];
$jumlahNilaiTotal = 0;
$jumlahMapelAdaNilai = 0;
foreach ($mapelList as $mapel) {
$tugas = $nilaiTugas->filter(function ($item) use ($mapel) {
return $item->tugas->materi->mapel_id == $mapel->id;
});
$rataTugas = $tugas->count() ? round($tugas->avg('nilai'), 1) : null;
$ujian = $nilaiUjian->filter(function ($item) use ($mapel) {
return $item->ujian->mapel_id == $mapel->id;
});
$rataUjian = $ujian->count() ? round($ujian->avg('nilai_total'), 1) : null;
$nilaiTotal = $nilaiTotalMapel[$mapel->id] ?? null;
if ($nilaiTotal !== null) {
$jumlahNilaiTotal += $nilaiTotal;
$jumlahMapelAdaNilai++;
}
$rataNilai[$mapel->id] = [
'rata_tugas' => $rataTugas,
'rata_ujian' => $rataUjian,
'nilai_total' => $nilaiTotal,
];
}
$nilaiTotalKeseluruhan = $jumlahMapelAdaNilai > 0 ? round($jumlahNilaiTotal / $jumlahMapelAdaNilai, 2) : null;
return view('siswa.evaluasi.mapel', compact('mapelList', 'rataNilai', 'nilaiTotalKeseluruhan','tahunAjaranAktif'));
}
public function detailEvaluasi($mapelId)
{
$siswa = Auth::user()->siswa;
// Ambil nilai ujian siswa khusus mapel ini
$nilaiUjian = $siswa->hasilUjian()
->whereHas('ujian', function($q) use ($mapelId) {
$q->where('mapel_id', $mapelId);
})
->with('ujian')
->get();
// Ambil nilai tugas siswa khusus mapel ini
$nilaiTugas = $siswa->tugasSiswa()
->whereHas('tugas.materi', function ($query) use ($mapelId) {
$query->where('mapel_id', $mapelId);
})
->with('tugas.materi')
->get();
// Hitung persentase pengerjaan
$totalUjian = $nilaiUjian->count();
$totalTugas = $nilaiTugas->count();
$selesaiUjian = $nilaiUjian->whereNotNull('nilai_total')->count();
$selesaiTugas = $nilaiTugas->whereNotNull('nilai')->count();
$persenUjian = $totalUjian ? round(($selesaiUjian / $totalUjian) * 100, 1) : 0;
$persenTugas = $totalTugas ? round(($selesaiTugas / $totalTugas) * 100, 1) : 0;
return view('siswa.evaluasi.detail', compact(
'nilaiUjian',
'nilaiTugas',
'persenUjian',
'persenTugas'
));
}
// Import di atas controller
public function exportPdf()
{
$siswa = Auth::user()->siswa()->with('kelas.mapel')->first();
if (!$siswa || !$siswa->kelas) {
abort(404, 'Data kelas siswa tidak ditemukan.');
}
$tahunAjaranAktif = TahunAjaran::where('status', 'aktif')->firstOrFail();
$mapelList = $siswa->kelas->mapel;
$nilaiTugas = TugasSiswa::where('siswa_id', $siswa->id)
->whereNotNull('nilai')
->with('tugas.materi')
->get();
$nilaiUjian = HasilUjian::where('siswa_id', $siswa->id)
->whereNotNull('nilai_total')
->with('ujian')
->get();
$nilaiTotalMapel = NilaiTotalMapel::where('siswa_id', $siswa->id)
->whereIn('mapel_id', $mapelList->pluck('id'))
->pluck('nilai_total', 'mapel_id')
->toArray();
$rataNilai = [];
$jumlahNilaiTotal = 0;
$jumlahMapelAdaNilai = 0;
foreach ($mapelList as $mapel) {
$tugas = $nilaiTugas->filter(fn($item) => $item->tugas->materi->mapel_id == $mapel->id);
$rataTugas = $tugas->count() ? round($tugas->avg('nilai'), 1) : null;
$ujian = $nilaiUjian->filter(fn($item) => $item->ujian->mapel_id == $mapel->id);
$rataUjian = $ujian->count() ? round($ujian->avg('nilai_total'), 1) : null;
$nilaiTotal = $nilaiTotalMapel[$mapel->id] ?? null;
if ($nilaiTotal !== null) {
$jumlahNilaiTotal += $nilaiTotal;
$jumlahMapelAdaNilai++;
}
$rataNilai[$mapel->id] = [
'rata_tugas' => $rataTugas,
'rata_ujian' => $rataUjian,
'nilai_total' => $nilaiTotal,
];
}
$nilaiTotalKeseluruhan = $jumlahMapelAdaNilai > 0 ? round($jumlahNilaiTotal / $jumlahMapelAdaNilai, 2) : null;
$pdf = PDF::loadView('siswa.evaluasi.pdf', compact(
'mapelList',
'rataNilai',
'nilaiTotalKeseluruhan',
'tahunAjaranAktif',
'siswa'
));
return $pdf->stream('evaluasi-nilai-' . $siswa->nama . '.pdf');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Ujian;
use App\Models\SoalPilgan;
use App\Models\SoalEssay;
use App\Models\JawabanPg;
use App\Models\JawabanEssay;
use Illuminate\Support\Facades\Auth;
class JawabanController extends Controller
{
public function kerjakan($ujian_id)
{
$ujian = Ujian::findOrFail($ujian_id);
$soalPg = SoalPilgan::where('ujian_id', $ujian_id)->get();
$soalEssay = SoalEssay::where('ujian_id', $ujian_id)->get();
return view('siswa.ujian.kerjakan', compact('ujian', 'soalPg', 'soalEssay'));
}
public function simpan(Request $request)
{
$siswa_id = Auth::guard('siswa')->id();
$ujian_id = $request->ujian_id;
// Simpan jawaban PG
if ($request->has('pg')) {
foreach ($request->pg as $soal_id => $jawaban) {
JawabanPg::updateOrCreate(
['siswa_id' => $siswa_id, 'ujian_id' => $ujian_id, 'soal_pg_id' => $soal_id],
['jawaban' => $jawaban]
);
}
}
// Simpan jawaban Essay
if ($request->has('essay')) {
foreach ($request->essay as $soal_id => $jawaban) {
JawabanEssay::updateOrCreate(
['siswa_id' => $siswa_id, 'ujian_id' => $ujian_id, 'soal_essay_id' => $soal_id],
['jawaban' => $jawaban]
);
}
}
return redirect()->route('siswa.dashboard')->with('success', 'Jawaban berhasil dikirim!');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\GuruMapel;
class ModulController extends Controller
{
public function index()
{
$user = Auth::user();
if (!$user->siswa || !$user->siswa->kelas_id) {
return 'Siswa belum memiliki data kelas';
}
$kelasId = $user->siswa->kelas_id;
$mapelTersedia = GuruMapel::with('mapel')
->where('kelas_id', $kelasId)
->get()
->pluck('mapel')
->unique('id')
->values();
return view('siswa.pelajaran.index', compact('mapelTersedia'));
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;
class ProfileSiswaController extends Controller
{
// Tampilkan profile siswa
public function index(): View
{
$user = Auth::user();
$siswa = $user->siswa; // pastikan relasi user-siswa sudah dibuat di model User
return view('siswa.profile-show', compact('user', 'siswa'));
}
// Form edit profile siswa
public function edit(): View
{
$user = Auth::user();
$siswa = $user->siswa;
return view('siswa.profile-edit', compact('user', 'siswa'));
}
// Update data profile siswa
public function update(Request $request)
{
$user = Auth::user();
$siswa = $user->siswa;
$request->validate([
'nama' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $user->id,
'nisn' => 'nullable|string|max:255',
'kelas_id' => 'nullable|integer|exists:kelas,id',
'jenis_kelamin' => 'required|in:L,P',
'alamat' => 'nullable|string',
'password' => 'nullable|string|min:6', // validasi password
]);
// Update user
$user->email = $request->email;
$user->name = $request->nama;
if ($request->filled('password')) {
$user->password = Hash::make($request->password);
}
$user->save();
// Update siswa
$siswa->nama = $request->nama;
$siswa->nisn = $request->nisn;
$siswa->kelas_id = $request->kelas_id;
$siswa->jenis_kelamin = $request->jenis_kelamin;
$siswa->alamat = $request->alamat;
$siswa->save();
return Redirect::route('siswa.profile.show')->with('status', 'Profil berhasil diperbarui.');
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\GuruMapel;
use App\Models\Materi;
use App\Models\TugasSiswa;
use App\Models\Tugas;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class SubjekController extends Controller
{
public function index(Request $request)
{
$user = Auth::user();
$siswa = $user->siswa;
if (!$siswa || !$siswa->kelas_id) {
abort(403, 'Siswa belum memiliki kelas.');
}
$mapelId = $request->mapel_id;
// Cek apakah mapel tersebut tersedia untuk kelas siswa
$validMapel = GuruMapel::where('kelas_id', $siswa->kelas_id)
->where('mapel_id', $mapelId)
->exists();
if (!$validMapel) {
abort(403, 'Akses ditolak: Mata pelajaran ini tidak tersedia untuk kelas Anda.');
}
// Ambil hanya materi yang tersedia untuk kelas ini dari tabel kelas_materi
$materiUtama = Materi::where('mapel_id', $mapelId)
->whereNull('parent_id')
->whereHas('kelas', function ($query) use ($siswa) {
$query->where('kelas_id', $siswa->kelas_id);
})
->orderBy('created_at')
->get();
$materiAnak = Materi::where('mapel_id', $mapelId)
->whereNotNull('parent_id')
->whereHas('kelas', function ($query) use ($siswa) {
$query->where('kelas_id', $siswa->kelas_id);
})
->orderBy('created_at')
->get();
return view('siswa.materi.index', compact('materiUtama', 'materiAnak'));
}
public function show($id)
{
$materi = Materi::findOrFail($id);
$user = Auth::user();
$siswa = $user->siswa;
if (!$siswa || !$siswa->kelas_id) {
abort(403, 'Siswa belum memiliki kelas.');
}
// Validasi akses materi berdasarkan kelas_materi
$valid = DB::table('kelas_materi')
->where('materi_id', $materi->id)
->where('kelas_id', $siswa->kelas_id)
->exists();
if (!$valid) {
abort(403, 'Akses ditolak: Materi ini tidak tersedia untuk kelas Anda.');
}
$tugas = Tugas::where('materi_id', $materi->id)->get();
$jawabanTugas = [];
foreach ($tugas as $t) {
$jawabanTugas[$t->id] = TugasSiswa::where('tugas_id', $t->id)
->where('siswa_id', $siswa->id)
->first();
}
return view('siswa.materi.show', compact('materi', 'tugas', 'jawabanTugas'));
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Tugas;
use App\Models\TugasSiswa;
use App\Models\Materi;
use Illuminate\Support\Facades\Auth;
class TugasSiswaController extends Controller
{
// Menampilkan daftar tugas dari suatu materi
public function index($materiId)
{
$siswa = Auth::user()->siswa;
if (!$siswa) abort(403, 'Siswa tidak ditemukan.');
// Validasi bahwa materi ada di kelas siswa
$materi = Materi::where('id', $materiId)
->where('kelas_id', $siswa->kelas_id)
->firstOrFail();
// Ambil tugas yang terkait dengan materi dan kelas siswa
$tugas = Tugas::with('kelas')
->where('materi_id', $materi->id)
->get()
->filter(function ($item) use ($siswa) {
return $item->kelas->contains('id', $siswa->kelas_id);
});
return view('siswa.tugas.index', compact('materi', 'tugas'));
}
// Menampilkan semua tugas untuk semua materi yang relevan dengan kelas siswa
public function allTugas()
{
$siswa = Auth::user()->siswa;
if (!$siswa) abort(403);
$kelasId = $siswa->kelas_id;
$tugas = Tugas::with(['materi', 'kelas'])
->whereHas('materi', function ($q) use ($kelasId) {
$q->where('kelas_id', $kelasId);
})
->latest()
->get()
->filter(function ($item) use ($kelasId) {
return $item->kelas->contains('id', $kelasId);
});
return view('siswa.tugas.index', compact('tugas'));
}
// Menampilkan detail materi dan tugas yang bisa dikumpulkan
public function show($materiId)
{
$siswa = Auth::user()->siswa;
if (!$siswa) abort(403, 'Data siswa tidak ditemukan.');
// Ambil materi yang sesuai kelas siswa
$materi = Materi::where('id', $materiId)
->where('kelas_id', $siswa->kelas_id)
->firstOrFail();
// Ambil tugas yang cocok untuk materi dan kelas siswa
$tugas = Tugas::with('kelas')
->where('materi_id', $materi->id)
->get()
->filter(function ($item) use ($siswa) {
return $item->kelas->contains('id', $siswa->kelas_id);
});
// Ambil jawaban tugas yang sudah dikirim siswa
$jawabanTugas = TugasSiswa::where('siswa_id', $siswa->id)
->whereIn('tugas_id', $tugas->pluck('id'))
->get()
->keyBy('tugas_id');
return view('siswa.materi.show', compact('materi', 'tugas', 'jawabanTugas'));
}
// Menyimpan jawaban tugas siswa
public function store(Request $request, $materiId, $tugasId)
{
$request->validate([
'jawaban' => 'nullable|string',
'file' => 'nullable|file|mimes:pdf,docx,jpg,png,mp4,mov|max:20480',
]);
$siswa = Auth::user()->siswa;
if (!$siswa) {
return back()->withErrors('Anda bukan siswa atau data siswa tidak ditemukan.');
}
$tugas = Tugas::with('kelas')
->where('id', $tugasId)
->where('materi_id', $materiId)
->firstOrFail();
if (!$tugas->kelas->contains('id', $siswa->kelas_id)) {
abort(403, 'Tugas ini tidak untuk kelas Anda.');
}
$data = [
'jawaban' => $request->jawaban,
];
if ($request->hasFile('file')) {
$data['file'] = $request->file('file')->store('jawaban_tugas', 'public');
}
TugasSiswa::updateOrCreate(
['tugas_id' => $tugas->id, 'siswa_id' => $siswa->id],
$data
);
return back()->with('success', 'Jawaban berhasil dikirim.');
}
public function nilaiTugas()
{
$siswa = Auth::user()->siswa;
if (!$siswa) abort(403);
// Ambil semua tugas siswa yang sudah dinilai (misal kolom 'nilai' di tabel tugas_siswa)
$nilaiTugas = TugasSiswa::with('tugas')
->where('siswa_id', $siswa->id)
->whereNotNull('nilai') // hanya yang sudah dinilai
->get();
// Hitung total nilai (optional, sesuaikan dengan logika kamu)
$totalNilai = $nilaiTugas->sum('nilai');
return view('siswa.nilai.tugas_nilai', compact('nilaiTugas', 'totalNilai'));
}
}

View File

@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers\Siswa;
use App\Http\Controllers\Controller;
use App\Models\Ujian;
use App\Models\HasilUjian;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class UjianSiswaController extends Controller
{
public function index()
{
$user = Auth::user();
$kelas_id = $user->siswa->kelas_id ?? null;
if (!$kelas_id) {
abort(403, 'User tidak memiliki kelas terkait.');
}
$ujians = Ujian::where('terbit', true)
->whereHas('kelas', function ($q) use ($kelas_id) {
$q->where('kelas.id', $kelas_id);
})
->with([
'mapel', // Tambahkan relasi mapel
'kelas' => function ($q) use ($kelas_id) {
$q->where('kelas.id', $kelas_id);
}
])
->get();
foreach ($ujians as $ujian) {
$ujian->deadline = optional($ujian->kelas->first())->pivot->deadline;
}
return view('siswa.ujian.index', compact('ujians'));
}
public function show($id)
{
$ujian = Ujian::with('kelas')->findOrFail($id);
$siswa = Auth::user()->siswa;
$kelas_id = $siswa->kelas_id ?? null;
if (!$kelas_id || !$ujian->kelas->contains('id', $kelas_id)) {
abort(403);
}
// Cek apakah sudah mengerjakan
$sudahDikerjakan = HasilUjian::where('ujian_id', $id)
->where('siswa_id', $siswa->id)
->exists();
// Ambil deadline dari pivot
$kelas = $ujian->kelas->where('id', $kelas_id)->first();
$deadline = optional($kelas->pivot)->deadline;
return view('siswa.ujian.show', compact('ujian', 'sudahDikerjakan', 'deadline'));
}
public function kerjakan($id)
{
$ujian = Ujian::with(['soalPilgan', 'soalEssay', 'kelas'])->findOrFail($id);
$siswa = Auth::user()->siswa;
if (!$siswa) abort(403, 'Data siswa tidak ditemukan.');
// Cek apakah kelas siswa ada di ujian dan ambil deadline
$kelas = $ujian->kelas->where('id', $siswa->kelas_id)->first();
if (!$kelas) abort(403, 'Ujian tidak tersedia untuk kelas Anda.');
$deadline = $kelas->pivot->deadline;
if ($deadline && now()->greaterThan($deadline)) {
return redirect()->route('siswa.ujian.index')
->with('error', 'Waktu pengerjaan ujian sudah lewat.');
}
$sudah = HasilUjian::where('ujian_id', $id)
->where('siswa_id', $siswa->id)
->exists();
if ($sudah) {
return redirect()->route('siswa.ujian.hasil', $id)
->with('info', 'Sudah dikerjakan.');
}
return view('siswa.ujian.kerjakan', compact('ujian'));
}
public function submit(Request $request, $id)
{
$ujian = Ujian::with(['soalPilgan'])->findOrFail($id);
$jawaban_pg = $request->input('pg', []);
$jawaban_essay = $request->input('essay', []);
$jumlah_soal_pg = $ujian->soalPilgan->count();
$bobot_per_soal = $jumlah_soal_pg > 0 ? $ujian->bobot_pg / $jumlah_soal_pg : 0;
$nilai_pg = 0;
foreach ($ujian->soalPilgan as $soal) {
$jawaban = $jawaban_pg[$soal->id] ?? null;
if ($jawaban !== null && strcasecmp($jawaban, $soal->jawaban_benar) === 0) {
$nilai_pg += $bobot_per_soal;
}
}
$nilai_pg = round($nilai_pg, 2);
$siswa_id = Auth::user()->siswa->id ?? null;
if (!$siswa_id) {
return redirect()->back()->with('error', 'Data siswa tidak ditemukan.');
}
HasilUjian::create([
'ujian_id' => $ujian->id,
'siswa_id' => $siswa_id,
'nilai_pg' => $nilai_pg,
'nilai_essay' => 0,
'nilai_total' => $nilai_pg,
'waktu_selesai' => now(),
]);
return redirect()->route('siswa.ujian.hasil', $ujian->id)
->with('success', 'Berhasil dikumpulkan.');
}
public function hasil($id)
{
$siswa_id = Auth::user()->siswa->id ?? null;
if (!$siswa_id) abort(403, 'Data siswa tidak ditemukan.');
$hasil = HasilUjian::with('ujian')
->where('ujian_id', $id)
->where('siswa_id', $siswa_id)
->firstOrFail();
return view('siswa.ujian.hasil', compact('hasil'));
}
public function evaluasi()
{
$siswa_id = Auth::user()->siswa->id ?? null;
if (!$siswa_id) abort(403, 'Data siswa tidak ditemukan.');
// Ambil semua hasil ujian milik siswa, dan ikutkan data ujian
$nilaiUjian = HasilUjian::with('ujian')
->where('siswa_id', $siswa_id)
->get();
// Hitung total nilai
$totalNilai = $nilaiUjian->sum('nilai_total');
return view('siswa.nilai.index', compact('nilaiUjian', 'totalNilai'));
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
public function handle($request, Closure $next, $role)
{
if (Auth::check() && Auth::user()->role === $role) {
return $next($request);
}
abort(403);
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
];
}
/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the login request is not rate limited.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout($this));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the rate limiting throttle key for the request.
*/
public function throttleKey(): string
{
return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class ProfileUpdateRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'lowercase',
'email',
'max:255',
Rule::unique(User::class)->ignore($this->user()->id),
],
];
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Imports;
use App\Models\Guru;
use App\Models\User;
use App\Models\TahunAjaran; // Add this line
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class GuruImport implements ToModel, WithHeadingRow
{
public function model(array $row)
{
// Check if email already exists in users
if (User::where('email', $row['email'])->exists()) {
// If it exists, skip importing this row or update as needed
return null;
}
// Find the TahunAjaran based on a unique identifier from your Excel file
// For example, if you have 'tahun_ajaran_nama' or 'tahun_ajaran_id' in your Excel
$tahunAjaran = TahunAjaran::where('tahun', $row['tahun_ajaran'])->first(); // Assuming 'tahun_ajaran' column in your Excel
// If the academic year doesn't exist, you might want to:
// 1. Skip this row (return null)
// 2. Create a new TahunAjaran (less common for import)
// 3. Log an error
if (!$tahunAjaran) {
// Option 1: Skip if Tahun Ajaran is not found
return null;
// Or throw an exception if you want strict validation
// throw new \Exception("Tahun Ajaran '{$row['tahun_ajaran']}' not found.");
}
// Create new user
$user = User::create([
'name' => $row['nama'],
'email' => $row['email'],
'password' => Hash::make($row['password'] ?? 'passworddefault'),
'role' => 'guru',
'is_active' => true,
]);
// Create guru data with user_id and tahun_ajaran_id
return new Guru([
'user_id' => $user->id,
'nip' => $row['nip'],
'nama' => $row['nama'],
'jenis_kelamin' => $row['jenis_kelamin'],
'jabatan' => $row['jabatan'],
'tahun_ajaran_id' => $tahunAjaran->id, // Assign the found tahun_ajaran_id
]);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Imports;
use App\Models\Siswa;
use App\Models\User;
use App\Models\Kelas;
use App\Models\TahunAjaran;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class SiswaImport implements ToModel, WithHeadingRow
{
public function model(array $row)
{
// Ambil tahun ajaran aktif
$tahunAktif = TahunAjaran::where('status', 'aktif')->first();
// Buat user untuk siswa
$user = User::create([
'name' => $row['nama'],
'email' => $row['email'],
'password' => Hash::make($row['password'] ?? 'defaultpassword'),
'role' => 'siswa',
'is_active' => 1,
]);
// Buat data siswa terkait user & tahun ajaran aktif
return new Siswa([
'user_id' => $user->id,
'nisn' => $row['nisn'],
'nama' => $row['nama'],
'alamat' => $row['alamat'],
'jenis_kelamin' => $row['jenis_kelamin'],
'kelas_id' => Kelas::where('nama_kelas', $row['kelas'])->value('id'),
'tahun_ajaran_id' => $tahunAktif->id ?? null,
]);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Imports;
use App\Models\SoalPilgan;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
class SoalPilganImport implements ToModel, WithHeadingRow, WithValidation
{
use Importable;
protected $ujian_id;
public function __construct($ujian_id)
{
$this->ujian_id = $ujian_id;
}
public function model(array $row)
{
return new SoalPilgan([
'ujian_id' => $this->ujian_id,
'pertanyaan' => $row['pertanyaan'],
'opsi_a' => $row['opsi_a'],
'opsi_b' => $row['opsi_b'],
'opsi_c' => $row['opsi_c'],
'opsi_d' => $row['opsi_d'],
'jawaban_benar' => $row['jawaban_benar'],
]);
}
public function rules(): array
{
return [
'*.pertanyaan' => 'required|string',
'*.opsi_a' => 'required|string',
'*.opsi_b' => 'required|string',
'*.opsi_c' => 'required|string',
'*.opsi_d' => 'required|string',
'*.jawaban_benar' => ['required', Rule::in(['a', 'b', 'c', 'd'])],
];
}
}

59
app/Mail/TestEmail.php Normal file
View File

@ -0,0 +1,59 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class TestEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct()
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Test Email',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'view.name',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
public function build()
{
return $this->subject('Coba Kirim dari Laravel Lokal')
->view('emails.test'); // buat file view ini
}
}

18
app/Mail/WelcomeMail.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class WelcomeMail extends Mailable
{
use Queueable, SerializesModels;
public function build()
{
return $this->subject('Selamat Datang di Aplikasi Kami')
->view('emails.welcome');
}
}

30
app/Models/Admin.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable; // Menurunkan dari Authenticatable
use Illuminate\Notifications\Notifiable;
class Admin extends Authenticatable // Menurunkan dari Authenticatable
{
use Notifiable;
protected $guard = 'admin'; // Sesuai dengan guard di config/auth.php
/**
* Mass assignable attributes.
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* Attributes that should be hidden.
*/
protected $hidden = [
'password',
'remember_token',
];
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class AngketSiswa extends Model
{
use HasFactory;
protected $table = 'angket_siswa';
protected $fillable = [
'siswa_id',
'nama', // nama siswa
'jenis_kelamin', // jenis kelamin siswa
'kelas_id',
'cita_cita',
'rencana_setelah_lulus',
'paket_pertama_id',
'paket_kedua_id',
'paket_ketiga_id',
'paket_final_id', // Pastikan ini ada di kolom database Anda
];
// --- PERBAIKAN DI SINI: Ubah nama metode relasi ke camelCase ---
// Relasi ke Paket Mapel pilihan pertama
public function paketPertama() // DIUBAH dari paket_pertama()
{
return $this->belongsTo(PaketMapel::class, 'paket_pertama_id');
}
// Relasi ke Paket Mapel pilihan kedua
public function paketKedua() // DIUBAH dari paket_kedua()
{
return $this->belongsTo(PaketMapel::class, 'paket_kedua_id');
}
// Relasi ke Paket Mapel pilihan ketiga
public function paketKetiga() // DIUBAH dari paket_ketiga()
{
return $this->belongsTo(PaketMapel::class, 'paket_ketiga_id');
}
// Relasi ke siswa
public function siswa()
{
return $this->belongsTo(Siswa::class, 'siswa_id');
}
// Relasi ke kelas
public function kelas()
{
return $this->belongsTo(Kelas::class, 'kelas_id');
}
// Relasi ke paket final
public function paketFinal() // DIUBAH dari paket_final()
{
return $this->belongsTo(PaketMapel::class, 'paket_final_id');
}
}

36
app/Models/Guru.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Guru extends Model
{
use HasFactory;
protected $table = 'guru';
protected $fillable = [
'user_id',
'nip',
'nama',
'jenis_kelamin',
'jabatan',
'tahun_ajaran_id',
];
public function tahunAjaran()
{
return $this->belongsTo(TahunAjaran::class);
}
// Relasi ke User
public function user()
{
return $this->belongsTo(User::class);
}
public function mapel()
{
return $this->belongsToMany(Mapel::class, 'guru_mapel', 'guru_id', 'mapel_id');
}
}

45
app/Models/GuruMapel.php Normal file
View File

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class GuruMapel extends Model
{
use HasFactory;
protected $table = 'guru_mapel';
protected $fillable = [
'guru_id',
'mapel_id',
'kelas_id',
'hari',
'jam_mulai',
'jam_selesai',
'tahun_ajaran_id'
];
public function guru()
{
return $this->belongsTo(Guru::class);
}
public function mapel()
{
return $this->belongsTo(Mapel::class);
}
public function kelas()
{
return $this->belongsTo(Kelas::class);
}
public function kelasMapel()
{
return $this->hasMany(KelasMapel::class, 'mapel_id', 'mapel_id');
}
}

26
app/Models/HasilUjian.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class HasilUjian extends Model
{
protected $fillable = ['ujian_id', 'siswa_id', 'nilai_pg', 'nilai_essay', 'nilai_total', 'waktu_selesai'];
public function ujian()
{
return $this->belongsTo(Ujian::class, 'ujian_id');
}
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

12
app/Models/InfoAngket.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InfoAngket extends Model
{
protected $table = 'info_angket';
protected $fillable = ['tata_cara'];
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JawabanEssay extends Model
{
use HasFactory;
protected $table = 'jawaban_essay';
protected $fillable = [
'siswa_id',
'ujian_id',
'soal_essay_id',
'jawaban',
];
}

19
app/Models/JawabanPG.php Normal file
View File

@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JawabanPG extends Model
{
use HasFactory;
protected $table = 'jawaban_pg';
protected $fillable = [
'siswa_id',
'ujian_id',
'soal_pg_id',
'jawaban',
];
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class JawabanSiswa extends Model
{
protected $table = 'jawaban_siswa';
protected $fillable = [
'siswa_id',
'soal_id',
'soal_tipe',
'jawaban',
];
public function siswa()
{
return $this->belongsTo(Siswa::class); // Asumsi ada model Siswa yang menghubungkan jawaban dengan siswa
}
}

62
app/Models/Kelas.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Kelas extends Model
{
use HasFactory;
protected $table = 'kelas';
protected $fillable = [
'nama_kelas',
'tingkat',
'jurusan',
];
public function ujian()
{
return $this->hasManyThrough(
\App\Models\Ujian::class, // model tujuan
\App\Models\KelasMapel::class, // model perantara
'kelas_id', // Foreign key di KelasMapel mengarah ke Kelas
'mapel_id', // Foreign key di Ujian mengarah ke Mapel
'id', // Local key di Kelas
'mapel_id' // Local key di KelasMapel yang cocok dengan mapel_id di Ujian
);
}
public function tahunAjaran()
{
return $this->belongsTo(TahunAjaran::class, 'tahun_ajaran_id');
}
public function mapel()
{
return $this->belongsToMany(Mapel::class, 'kelas_mapel');
}
public function tugas()
{
return $this->belongsToMany(Tugas::class, 'tugas_kelas')
->withPivot('deadline')
->withTimestamps();
}
public function materi()
{
return $this->belongsToMany(Materi::class, 'kelas_materi', 'kelas_id', 'materi_id')
->withPivot('tanggal_terbit')
->withTimestamps();
}
public function siswa()
{
return $this->hasMany(Siswa::class, 'kelas_id', 'id');
}
}

40
app/Models/KelasMapel.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class KelasMapel extends Model
{
use HasFactory;
protected $table = 'kelas_mapel';
// Kolom yang bisa diisi
protected $fillable = ['kelas_id', 'mapel_id', 'tahun_ajaran_id'];
// Relasi ke model Kelas
public function kelas()
{
return $this->belongsTo(Kelas::class);
}
// Relasi ke model Mapel
public function mapel()
{
return $this->belongsTo(Mapel::class);
}
// Relasi ke model TahunAjaran
public function tahunAjaran()
{
return $this->belongsTo(TahunAjaran::class);
}
// Opsional
public function guruMapel()
{
return $this->belongsTo(GuruMapel::class, 'mapel_id', 'mapel_id');
}
}

40
app/Models/Mapel.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Mapel extends Model
{
use HasFactory;
protected $table = 'mapels';
protected $fillable = [
'nama_mapel',
];
// App\Models\Mapel.php
public function kelas()
{
return $this->belongsToMany(Kelas::class, 'kelas_mapel');
}
public function paketMapel()
{
return $this->belongsToMany(PaketMapel::class, 'paket_mapel_detail');
}
public function guru()
{
return $this->belongsToMany(Guru::class, 'guru_mapel');
}
public function guruMapel()
{
// 'mapel_id' is the foreign key on the 'guru_mapel' table that links back to 'mapels' table.
return $this->hasMany(GuruMapel::class, 'mapel_id');
}
}

62
app/Models/Materi.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Materi extends Model
{
protected $table = 'materi';
protected $fillable = [
'guru_id',
'mapel_id',
'parent_id',
'judul',
'isi',
'file',
];
public function mapel()
{
return $this->belongsTo(Mapel::class);
}
public function guru()
{
return $this->belongsTo(Guru::class);
}
public function parent()
{
return $this->belongsTo(Materi::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Materi::class, 'parent_id');
}
public function tugas()
{
return $this->hasMany(Tugas::class);
}
public function forumDiskusi()
{
return $this->hasMany(\App\Models\ForumDiskusi::class);
}
public function guruMapel()
{
return $this->belongsTo(\App\Models\GuruMapel::class);
}
// app/Models/Materi.php
public function kelas()
{
return $this->belongsToMany(Kelas::class, 'kelas_materi', 'materi_id', 'kelas_id')
->withPivot('tanggal_terbit')
->withTimestamps();
}
}

24
app/Models/NilaiSikap.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class NilaiSikap extends Model
{
use HasFactory;
protected $table = 'nilai_sikap';
protected $fillable = [
'siswa_id',
'nilai',
];
// Relasi ke siswa
public function siswa()
{
return $this->belongsTo(Siswa::class, 'siswa_id');
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class NilaiTotalMapel extends Model
{
protected $table = 'nilai_total_mapel'; // nama tabel di database
protected $fillable = [
'siswa_id',
'mapel_id',
'nilai_total',
];
// Jika kamu pakai timestamps di tabel, biarkan, kalau tidak ada, matikan:
public $timestamps = false;
// Relasi ke Siswa
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
// Relasi ke Mapel
public function mapel()
{
return $this->belongsTo(Mapel::class);
}
}

42
app/Models/PaketMapel.php Normal file
View File

@ -0,0 +1,42 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class PaketMapel extends Model
{
use HasFactory;
protected $table = 'paket_mapel';
protected $fillable = ['nama_paket', 'deskripsi', 'kuota'];
// Relasi ke angket siswa (model AngketSiswa)
public function angketSiswa()
{
return $this->hasMany(AngketSiswa::class);
}
// Relasi many-to-many ke siswa lewat tabel angket_siswa sebagai pivot
public function siswas()
{
return $this->belongsToMany(
Siswa::class,
'angket_siswa', // tabel pivot
'paket_mapel_id', // foreign key di pivot yang mengarah ke paket_mapel
'siswa_id' // foreign key di pivot yang mengarah ke siswa
);
}
public function mapels()
{
return $this->belongsToMany(Mapel::class, 'paket_mapel_detail');
}
public function peringkat()
{
return $this->hasMany(PeringkatPaketMapel::class);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PaketMapelDetail extends Model
{
protected $table = 'paket_mapel_detail';
protected $fillable = ['paket_mapel_id', 'mapel_id'];
public function paket()
{
return $this->belongsTo(PaketMapel::class);
}
public function mapel()
{
return $this->belongsTo(Mapel::class);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PengaturanBobot extends Model
{
protected $table = 'pengaturan_bobot';
protected $fillable = ['guru_id', 'kelas_id', 'bobot_tugas', 'bobot_ujian', 'bobot_sikap'];
public function guru() { return $this->belongsTo(Guru::class); }
public function kelas() { return $this->belongsTo(Kelas::class); }
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PeringkatPaketMapel extends Model
{
protected $fillable = ['siswa_id', 'paket_mapel_id', 'nilai_total', 'peringkat'];
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
public function paket()
{
return $this->belongsTo(PaketMapel::class, 'paket_mapel_id');
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ProdiLanjutan extends Model
{
protected $table = 'prodi_lanjutan';
protected $fillable = ['nama'];
public function mapels()
{
return $this->belongsToMany(Mapel::class, 'mapel_prodi', 'prodi_lanjutan_id', 'mapel_id');
}
}

12
app/Models/Setting.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
protected $fillable = ['key', 'value'];
public $timestamps = true;
}

92
app/Models/Siswa.php Normal file
View File

@ -0,0 +1,92 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Kelas;
use App\Models\TugasSiswa;
use App\Models\PengumpulanTugas;
use App\Models\HasilUjian;
use App\Models\NilaiSikap;
class Siswa extends Model
{
use HasFactory;
protected $table = 'siswa';
protected $fillable = [
'user_id',
'nisn',
'nama',
'kelas_id',
'jenis_kelamin',
'alamat',
'paket_mapel_id',
'tahun_ajaran_id',
];
// Relasi ke tabel users
public function user()
{
return $this->belongsTo(User::class);
}
//thun ajar
public function tahunAjaran()
{
return $this->belongsTo(TahunAjaran::class, 'tahun_ajaran_id');
}
// Relasi ke kelas
public function kelas()
{
return $this->belongsTo(Kelas::class);
}
// app/Models/Siswa.php
public function nilaiTotalMapel()
{
return $this->hasMany(NilaiTotalMapel::class, 'siswa_id');
}
// Relasi ke hasil ujian
public function hasilUjian()
{
return $this->hasMany(HasilUjian::class, 'siswa_id');
}
// Relasi ke tugas siswa (kalau dipakai)
public function tugasSiswa()
{
return $this->hasMany(TugasSiswa::class, 'siswa_id');
}
// Relasi ke pengumpulan tugas
public function pengumpulanTugas()
{
return $this->hasMany(TugasSiswa::class, 'siswa_id', 'id');
}
// Relasi ke nilai sikap
public function nilaiSikap()
{
return $this->hasOne(NilaiSikap::class, 'siswa_id');
}
public function paketMapel()
{
// Asumsikan foreign key di tabel siswa adalah 'paket_mapel_id'
return $this->belongsTo(PaketMapel::class, 'paket_mapel_id', 'id');
}
public function peringkatPaket()
{
return $this->hasOne(PeringkatPaketMapel::class);
}
public function angket()
{
return $this->hasOne(AngketSiswa::class, 'siswa_id', 'id');
}
}

18
app/Models/SoalEssay.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SoalEssay extends Model
{
use HasFactory;
protected $table = 'soal_essay';
protected $fillable = ['ujian_id', 'pertanyaan', 'kunci_jawaban'];
public function ujian() {
return $this->belongsTo(Ujian::class);
}
}

18
app/Models/SoalPilgan.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SoalPilgan extends Model
{
protected $table = 'soal_pilgan';
use HasFactory;
protected $fillable = ['ujian_id', 'pertanyaan', 'opsi_a', 'opsi_b', 'opsi_c', 'opsi_d', 'jawaban_benar'];
public function ujian() {
return $this->belongsTo(Ujian::class);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TahunAjaran extends Model
{
protected $table = 'tahun_ajaran';
// Kolom yang bisa diisi massal
protected $fillable = [
'tahun', // varchar(20), contoh: "2024/2025"
'semester', // enum('ganjil', 'genap')
'status' // enum('aktif', 'tidak aktif', 'lulus')
];
// Jika ingin menggunakan enum sebagai konstanta di model, bisa tambahkan ini (opsional):
const STATUS_AKTIF = 'aktif';
const STATUS_TIDAK_AKTIF = 'tidak aktif';
const STATUS_LULUS = 'lulus';
const SEMESTER_GANJIL = 'ganjil';
const SEMESTER_GENAP = 'genap';
public function kelas()
{
return $this->hasMany(Kelas::class, 'tahun_ajaran_id');
}
}

46
app/Models/Tugas.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tugas extends Model
{
use HasFactory;
protected $table = 'tugas';
protected $fillable = [
'materi_id',
'judul',
'deskripsi',
'file',
'deadline', // walau ini nullable dan biasanya null karena deadline per kelas
];
public function tugasKelas(): HasMany
{
return $this->hasMany(\App\Models\TugasKelas::class);
}
// Relasi ke Materi
public function materi()
{
return $this->belongsTo(Materi::class, 'materi_id');
}
// Relasi many-to-many ke Kelas lewat pivot tugas_kelas
public function kelas()
{
return $this->belongsToMany(Kelas::class, 'tugas_kelas')
->withPivot('deadline')
->withTimestamps();
}
// Relasi ke jawaban siswa (pengumpulan tugas)
public function pengumpulan()
{
return $this->hasMany(TugasSiswa::class, 'tugas_id');
}
}

25
app/Models/TugasKelas.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TugasKelas extends Model
{
protected $table = 'tugas_kelas';
protected $fillable = ['tugas_id', 'kelas_id', 'deadline'];
public $timestamps = true;
public function tugas()
{
return $this->belongsTo(Tugas::class);
}
public function kelas()
{
return $this->belongsTo(Kelas::class);
}
}

27
app/Models/TugasSiswa.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TugasSiswa extends Model
{
protected $table = 'tugas_siswa'; // nama tabel di database
protected $fillable = ['tugas_id', 'siswa_id', 'jawaban', 'file', 'nilai', 'catatan_guru'];
// Jika kamu pakai timestamps, pastikan ada kolom created_at dan updated_at di tabel
public $timestamps = true;
// Relasi ke tugas
public function tugas()
{
return $this->belongsTo(Tugas::class);
}
// Relasi ke siswa
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

58
app/Models/Ujian.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Ujian extends Model
{
use HasFactory;
protected $fillable = [
'mapel_id',
'judul',
'keterangan',
'tanggal_post',
'waktu',
'soal',
'terbit',
'bobot_pg',
'bobot_essay',
];
protected $casts = [
'tanggal_post' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function soalPilgan()
{
return $this->hasMany(SoalPilgan::class);
}
public function soalEssay()
{
return $this->hasMany(SoalEssay::class);
}
public function mapel()
{
return $this->belongsTo(Mapel::class, 'mapel_id');
}
public function hasilUjian()
{
return $this->hasMany(HasilUjian::class);
}
public function kelas()
{
return $this->belongsToMany(Kelas::class, 'kelas_ujian')
->withPivot('deadline')
->withTimestamps();
}
}

69
app/Models/User.php Normal file
View File

@ -0,0 +1,69 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'role',
'is_active',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function guru()
{
return $this->hasOne(Guru::class);
}
public function siswa()
{
return $this->hasOne(Siswa::class);
}
// app/Models/User.php
public function tahunAjaranAktif()
{
return $this->hasOne(TahunAjaran::class, 'id', 'tahun_ajaran_id')->where('is_aktif', true);
}
public function mapel()
{
return $this->belongsToMany(Mapel::class, 'guru_mapel', 'guru_id', 'mapel_id');
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Providers;
use App\Models\TahunAjaran;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot()
{
View::composer('*', function ($view) {
$tahunAktif = TahunAjaran::where('status', 'aktif')->first();
$view->with('tahunAktif', $tahunAktif);
});
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class AppLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.app');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class GuestLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.guest');
}
}

15
artisan Normal file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
->handleCommand(new ArgvInput);
exit($status);

22
bootstrap/app.php Normal file
View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// Daftarkan middleware route kustom di sini
$middleware->alias([
'role' => \App\Http\Middleware\RoleMiddleware::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

2
bootstrap/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

5
bootstrap/providers.php Normal file
View File

@ -0,0 +1,5 @@
<?php
return [
App\Providers\AppServiceProvider::class,
];

75
composer.json Normal file
View File

@ -0,0 +1,75 @@
{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.2",
"barryvdh/laravel-dompdf": "^3.1",
"laravel/framework": "^11.0",
"laravel/tinker": "^2.9",
"maatwebsite/excel": "^3.1",
"s-ichikawa/laravel-sendgrid-driver": "^4.0",
"symfony/mailer": "^7.3"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/breeze": "^2.3",
"laravel/pint": "^1.13",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.0",
"pestphp/pest": "^3.8",
"pestphp/pest-plugin-laravel": "^3.2",
"spatie/laravel-ignition": "^2.4"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --ansi"
]
},
"extra": {
"branch-alias": {
"dev-master": "11.x-dev"
},
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

10409
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

128
config/app.php Normal file
View File

@ -0,0 +1,128 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application, which will be used when the
| framework needs to place the application's name in a notification or
| other UI elements where an application name needs to be displayed.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| the application so that it's available within Artisan commands.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. The timezone
| is set to "UTC" by default as it is suitable for most use cases.
|
*/
'timezone' => env('APP_TIMEZONE', 'UTC'),
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by Laravel's translation / localization methods. This option can be
| set to any locale for which you plan to have translation strings.
|
*/
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is utilized by Laravel's encryption services and should be set
| to a random, 32 character string to ensure that all encrypted values
| are secure. You should do this prior to deploying the application.
|
*/
'cipher' => 'AES-256-CBC',
'key' => env('APP_KEY'),
'previous_keys' => [
...array_filter(
explode(',', env('APP_PREVIOUS_KEYS', ''))
),
],
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
'store' => env('APP_MAINTENANCE_STORE', 'database'),
],
];

Some files were not shown because too many files have changed in this diff Show More