udpate : create siswa (foto siswa)
This commit is contained in:
parent
4c77ade45f
commit
d2bc200b8c
|
@ -3,13 +3,58 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\AbsensiSiswa;
|
use App\Models\AbsensiSiswa;
|
||||||
use Illuminate\Http\Request;
|
use App\Models\Jurusan;
|
||||||
|
use App\Models\Kelas;
|
||||||
|
use App\Models\Devices;
|
||||||
|
use Illuminate\Http\Request; // Import Request
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class AbsensiSiswaController extends Controller
|
class AbsensiSiswaController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
/**
|
||||||
|
* Menampilkan halaman presensi siswa dengan filter dinamis.
|
||||||
|
*/
|
||||||
|
public function index(Request $request) // Tambahkan Request
|
||||||
{
|
{
|
||||||
$absensi = AbsensiSiswa::with('siswa')->latest()->get();
|
// Mulai query builder
|
||||||
return view('admin.presensi.siswa', compact('absensi'));
|
$query = AbsensiSiswa::with(['siswa.jurusan', 'siswa.kelas', 'devices']);
|
||||||
|
|
||||||
|
// 1. Filter berdasarkan Tanggal
|
||||||
|
if ($request->filled('tanggal')) {
|
||||||
|
$query->whereDate('waktu', $request->tanggal);
|
||||||
|
} else {
|
||||||
|
// Default jika tidak ada filter tanggal: hari ini
|
||||||
|
$query->whereDate('waktu', Carbon::today());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Filter berdasarkan Jurusan
|
||||||
|
// 'whereHas' digunakan untuk filter berdasarkan relasi
|
||||||
|
if ($request->filled('jurusan_id') && $request->jurusan_id != 'all') {
|
||||||
|
$query->whereHas('siswa', function ($q) use ($request) {
|
||||||
|
$q->where('id_jurusan', $request->jurusan_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Filter berdasarkan Kelas
|
||||||
|
if ($request->filled('kelas_id') && $request->kelas_id != 'all') {
|
||||||
|
$query->whereHas('siswa', function ($q) use ($request) {
|
||||||
|
$q->where('id_kelas', $request->kelas_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Filter berdasarkan Device
|
||||||
|
if ($request->filled('device_id') && $request->device_id != 'all') {
|
||||||
|
$query->where('id_devices', $request->device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eksekusi query setelah semua filter diterapkan
|
||||||
|
$absensi = $query->latest('waktu')->get();
|
||||||
|
|
||||||
|
// Data untuk mengisi dropdown filter tetap sama
|
||||||
|
$jurusans = Jurusan::orderBy('nama_jurusan')->get();
|
||||||
|
$kelases = Kelas::orderBy('nama_kelas')->get();
|
||||||
|
$devices = Devices::orderBy('nama_device')->get();
|
||||||
|
|
||||||
|
return view('admin.presensi.siswa', compact('absensi', 'jurusans', 'kelases', 'devices'));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,10 +3,12 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\FotoSiswa;
|
||||||
use App\Models\Siswa;
|
use App\Models\Siswa;
|
||||||
use App\Models\Kelas;
|
use App\Models\Kelas;
|
||||||
use App\Models\Jurusan;
|
use App\Models\Jurusan;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
@ -40,12 +42,15 @@ public function create()
|
||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
// Validasi data
|
// Validasi data: 'foto_siswa' sekarang adalah array
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'foto_siswa' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
|
||||||
'nama_siswa' => 'required|string|max:255',
|
'nama_siswa' => 'required|string|max:255',
|
||||||
'nisn' => 'required|numeric|unique:siswa,nisn',
|
'nisn' => 'required|numeric|unique:siswa,nisn',
|
||||||
'tanggal_lahir' => 'required|date',
|
'tanggal_lahir' => 'required|date',
|
||||||
|
// Validasi untuk array file
|
||||||
|
'foto_siswa' => 'required|array|min:1', // Wajib ada minimal 1 foto
|
||||||
|
'foto_siswa.*' => 'required|image|mimes:jpeg,png,jpg|max:2048', // Validasi setiap file dalam array
|
||||||
|
// ... (validasi lainnya sama)
|
||||||
'jenis_kelamin' => 'required|in:L,P',
|
'jenis_kelamin' => 'required|in:L,P',
|
||||||
'email' => 'required|email|unique:siswa,email',
|
'email' => 'required|email|unique:siswa,email',
|
||||||
'no_hp' => 'required|string|max:15',
|
'no_hp' => 'required|string|max:15',
|
||||||
|
@ -55,34 +60,47 @@ public function store(Request $request)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return redirect()->back()
|
return redirect()->back()->withErrors($validator)->withInput();
|
||||||
->withErrors($validator)
|
|
||||||
->withInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload foto siswa
|
// Gunakan transaction untuk memastikan semua data tersimpan atau tidak sama sekali
|
||||||
if ($request->hasFile('foto_siswa')) {
|
DB::beginTransaction();
|
||||||
$foto = $request->file('foto_siswa');
|
try {
|
||||||
$fotoName = time() . '_' . $foto->getClientOriginalName();
|
// 1. Simpan data siswa terlebih dahulu (tanpa foto)
|
||||||
$fotoPath = $foto->storeAs('siswa', $fotoName, 'public');
|
$siswa = Siswa::create([
|
||||||
|
'nama_siswa' => $request->nama_siswa,
|
||||||
|
'nisn' => $request->nisn,
|
||||||
|
'tanggal_lahir' => $request->tanggal_lahir,
|
||||||
|
'jenis_kelamin' => $request->jenis_kelamin,
|
||||||
|
'email' => $request->email,
|
||||||
|
'no_hp' => $request->no_hp,
|
||||||
|
'alamat' => $request->alamat,
|
||||||
|
'id_kelas' => $request->id_kelas,
|
||||||
|
'id_jurusan' => $request->id_jurusan,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Loop dan simpan setiap foto yang di-upload
|
||||||
|
if ($request->hasFile('foto_siswa')) {
|
||||||
|
foreach ($request->file('foto_siswa') as $foto) {
|
||||||
|
$fotoName = $siswa->id . '_' . time() . '_' . $foto->getClientOriginalName();
|
||||||
|
$path = $foto->storeAs('siswa_fotos', $fotoName, 'public');
|
||||||
|
|
||||||
|
// Buat record di tabel foto_siswa
|
||||||
|
FotoSiswa::create([
|
||||||
|
'id_siswa' => $siswa->id,
|
||||||
|
'path' => $path,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit(); // Jika semua berhasil, simpan perubahan
|
||||||
|
|
||||||
|
return redirect()->route('admin.siswa.index')->with('success', 'Siswa baru berhasil didaftarkan!');
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack(); // Jika ada error, batalkan semua
|
||||||
|
return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage())->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simpan data siswa
|
|
||||||
$siswa = new Siswa();
|
|
||||||
$siswa->foto_siswa = $fotoPath ?? null;
|
|
||||||
$siswa->nama_siswa = $request->nama_siswa;
|
|
||||||
$siswa->nisn = $request->nisn;
|
|
||||||
$siswa->tanggal_lahir = $request->tanggal_lahir;
|
|
||||||
$siswa->jenis_kelamin = $request->jenis_kelamin;
|
|
||||||
$siswa->email = $request->email;
|
|
||||||
$siswa->no_hp = $request->no_hp;
|
|
||||||
$siswa->alamat = $request->alamat;
|
|
||||||
$siswa->id_kelas = $request->id_kelas;
|
|
||||||
$siswa->id_jurusan = $request->id_jurusan;
|
|
||||||
$siswa->save();
|
|
||||||
|
|
||||||
return redirect()->route('admin.siswa.index')
|
|
||||||
->with('success', 'Siswa baru berhasil didaftarkan!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ class AbsensiSiswa extends Model
|
||||||
//Relasi ke tabel devices
|
//Relasi ke tabel devices
|
||||||
public function devices()
|
public function devices()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Device::class, 'id_devices');
|
return $this->belongsTo(Devices::class, 'id_devices');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relasi ke tabel siswa
|
// Relasi ke tabel siswa
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class FotoSiswa extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'foto_siswa';
|
||||||
|
protected $fillable = ['id_siswa', 'path'];
|
||||||
|
|
||||||
|
// Relasi balik ke Siswa
|
||||||
|
public function siswa()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Siswa::class, 'id_siswa');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessor untuk mendapatkan URL lengkap ke foto
|
||||||
|
public function getUrlAttribute()
|
||||||
|
{
|
||||||
|
return Storage::url($this->path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ class Siswa extends Model
|
||||||
'nama_siswa',
|
'nama_siswa',
|
||||||
'nisn',
|
'nisn',
|
||||||
'tanggal_lahir',
|
'tanggal_lahir',
|
||||||
'foto_siswa',
|
|
||||||
'jenis_kelamin',
|
'jenis_kelamin',
|
||||||
'alamat',
|
'alamat',
|
||||||
'no_hp',
|
'no_hp',
|
||||||
|
|
|
@ -16,7 +16,6 @@ public function up(): void
|
||||||
$table->string('nama_siswa');
|
$table->string('nama_siswa');
|
||||||
$table->integer('nisn')->unique();
|
$table->integer('nisn')->unique();
|
||||||
$table->date('tanggal_lahir');
|
$table->date('tanggal_lahir');
|
||||||
$table->string('foto_siswa');
|
|
||||||
$table->enum('jenis_kelamin', ['L', 'P']);
|
$table->enum('jenis_kelamin', ['L', 'P']);
|
||||||
$table->text('alamat');
|
$table->text('alamat');
|
||||||
$table->string('no_hp');
|
$table->string('no_hp');
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Tabel baru untuk menyimpan path ke banyak foto untuk setiap siswa
|
||||||
|
Schema::create('foto_siswa', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
// Menghubungkan ke tabel 'siswa'. Jika siswa dihapus, fotonya juga ikut terhapus.
|
||||||
|
$table->foreignId('id_siswa')->constrained('siswa')->onDelete('cascade');
|
||||||
|
// Kolom untuk menyimpan path file foto di storage
|
||||||
|
$table->string('path');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('foto_siswa');
|
||||||
|
}
|
||||||
|
};
|
|
@ -18,7 +18,6 @@ public function run(): void
|
||||||
'nama_siswa' => 'Aldo Wijaya',
|
'nama_siswa' => 'Aldo Wijaya',
|
||||||
'nisn' => '1234567890',
|
'nisn' => '1234567890',
|
||||||
'tanggal_lahir' => '2006-05-12',
|
'tanggal_lahir' => '2006-05-12',
|
||||||
'foto_siswa' => 'aldo.jpg',
|
|
||||||
'jenis_kelamin' => 'L',
|
'jenis_kelamin' => 'L',
|
||||||
'alamat' => 'Jl. Merdeka No. 1',
|
'alamat' => 'Jl. Merdeka No. 1',
|
||||||
'no_hp' => '081234567890',
|
'no_hp' => '081234567890',
|
||||||
|
@ -30,7 +29,6 @@ public function run(): void
|
||||||
'nama_siswa' => 'Salsa Mutiara',
|
'nama_siswa' => 'Salsa Mutiara',
|
||||||
'nisn' => '1234567891',
|
'nisn' => '1234567891',
|
||||||
'tanggal_lahir' => '2006-03-25',
|
'tanggal_lahir' => '2006-03-25',
|
||||||
'foto_siswa' => 'salsa.jpg',
|
|
||||||
'jenis_kelamin' => 'P',
|
'jenis_kelamin' => 'P',
|
||||||
'alamat' => 'Jl. Mawar No. 7',
|
'alamat' => 'Jl. Mawar No. 7',
|
||||||
'no_hp' => '081234567891',
|
'no_hp' => '081234567891',
|
||||||
|
@ -42,7 +40,6 @@ public function run(): void
|
||||||
'nama_siswa' => 'Raihan Pratama',
|
'nama_siswa' => 'Raihan Pratama',
|
||||||
'nisn' => '1234567892',
|
'nisn' => '1234567892',
|
||||||
'tanggal_lahir' => '2006-01-10',
|
'tanggal_lahir' => '2006-01-10',
|
||||||
'foto_siswa' => 'raihan.jpg',
|
|
||||||
'jenis_kelamin' => 'L',
|
'jenis_kelamin' => 'L',
|
||||||
'alamat' => 'Jl. Kenanga No. 3',
|
'alamat' => 'Jl. Kenanga No. 3',
|
||||||
'no_hp' => '081234567892',
|
'no_hp' => '081234567892',
|
||||||
|
|
|
@ -3,108 +3,179 @@
|
||||||
@section('title', 'Smart School | Presensi Siswa')
|
@section('title', 'Smart School | Presensi Siswa')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
<div class="bg-gray-50 min-h-screen p-4 sm:p-6 lg:p-8">
|
||||||
|
<div class="max-w-7xl mx-auto">
|
||||||
|
|
||||||
<div class="container">
|
<div class="mb-6 flex flex-col sm:flex-row justify-between items-center">
|
||||||
<h2 class="mb-6 text-3xl font-bold text-gray-800">Presensi Siswa</h2>
|
<h1 class="text-3xl font-bold text-gray-800">Halaman Presensi Siswa</h1>
|
||||||
|
<span class="text-sm font-medium text-gray-500 mt-2 sm:mt-0">Real-time Face Recognition</span>
|
||||||
<!-- Pilihan Kelas -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="kelas" class="block text-lg font-semibold text-gray-700">Pilih Kelas:</label>
|
|
||||||
<select id="kelas"
|
|
||||||
class="form-select w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500">
|
|
||||||
<option value="all">Semua Kelas</option>
|
|
||||||
<option value="kelas_10">Kelas 10</option>
|
|
||||||
<option value="kelas_11">Kelas 11</option>
|
|
||||||
<option value="kelas_12">Kelas 12</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter Tanggal -->
|
<!-- Grid Layout Utama -->
|
||||||
<div class="mb-4">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<label for="tanggal" class="block text-lg font-semibold text-gray-700">Filter Tanggal:</label>
|
|
||||||
<input type="date" id="tanggal"
|
|
||||||
class="form-control w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Kotak Video Stream -->
|
<!-- Kolom Kiri: Filter & Tabel Presensi -->
|
||||||
<div class="mb-6 text-center">
|
<div class="lg:col-span-2 space-y-6">
|
||||||
<h5 class="text-xl font-semibold text-blue-600">Live Camera</h5>
|
|
||||||
<div id="cameraContainer"
|
<!-- Card untuk Filter -->
|
||||||
class="border rounded-lg shadow-lg mx-auto w-[640px] h-[480px] flex items-center justify-center bg-gray-200">
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200">
|
||||||
<img id="cameraFeed" src="http://localhost:5000/video_feed" width="640" height="480" class="hidden">
|
<h2 class="text-xl font-semibold text-gray-700 mb-4">Filter Data</h2>
|
||||||
<p id="cameraStatus" class="text-gray-500">Memeriksa kamera...</p>
|
|
||||||
|
{{-- Bungkus filter dalam form GET agar bisa dibaca controller --}}
|
||||||
|
<form id="filterForm" action="{{ url()->current() }}" method="GET">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
|
<!-- Filter Jurusan -->
|
||||||
|
<div>
|
||||||
|
<label for="jurusan" class="block text-sm font-medium text-gray-600 mb-1">Pilih Jurusan</label>
|
||||||
|
{{-- Tambahkan atribut 'name' agar bisa dibaca oleh Request --}}
|
||||||
|
<select id="jurusan" name="jurusan_id" class="filter-input w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition">
|
||||||
|
<option value="all">Semua Jurusan</option>
|
||||||
|
@foreach($jurusans as $jurusan)
|
||||||
|
{{-- Jaga agar nilai filter tetap terpilih setelah halaman di-refresh --}}
|
||||||
|
<option value="{{ $jurusan->id }}" {{ request('jurusan_id') == $jurusan->id ? 'selected' : '' }}>
|
||||||
|
{{ $jurusan->nama_jurusan }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Filter Kelas -->
|
||||||
|
<div>
|
||||||
|
<label for="kelas" class="block text-sm font-medium text-gray-600 mb-1">Pilih Kelas</label>
|
||||||
|
<select id="kelas" name="kelas_id" class="filter-input w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition">
|
||||||
|
<option value="all">Semua Kelas</option>
|
||||||
|
@foreach($kelases as $kelas)
|
||||||
|
<option value="{{ $kelas->id }}" {{ request('kelas_id') == $kelas->id ? 'selected' : '' }}>
|
||||||
|
{{ $kelas->nama_kelas }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Filter Ruangan/Device -->
|
||||||
|
<div>
|
||||||
|
<label for="device" class="block text-sm font-medium text-gray-600 mb-1">Pilih Device</label>
|
||||||
|
<select id="device" name="device_id" class="filter-input w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition">
|
||||||
|
<option value="all">Semua Device</option>
|
||||||
|
@foreach($devices as $device)
|
||||||
|
<option value="{{ $device->id }}" {{ request('device_id') == $device->id ? 'selected' : '' }}>
|
||||||
|
{{ $device->nama_device }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Filter Tanggal -->
|
||||||
|
<div>
|
||||||
|
<label for="tanggal" class="block text-sm font-medium text-gray-600 mb-1">Pilih Tanggal</label>
|
||||||
|
<input type="date" id="tanggal" name="tanggal" value="{{ request('tanggal', date('Y-m-d')) }}" class="filter-input w-full p-2 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Card untuk Tabel Presensi -->
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200">
|
||||||
|
<h2 id="laporan-title" class="text-xl font-semibold text-gray-700 mb-4">Laporan Kehadiran</h2>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm text-left text-gray-500">
|
||||||
|
<thead class="text-xs text-gray-700 uppercase bg-gray-100">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="px-6 py-3 rounded-l-lg">No</th>
|
||||||
|
<th scope="col" class="px-6 py-3">Nama Siswa</th>
|
||||||
|
<th scope="col" class="px-6 py-3">Jurusan</th>
|
||||||
|
<th scope="col" class="px-6 py-3">Kelas</th>
|
||||||
|
<th scope="col" class="px-6 py-3">Waktu Presensi</th>
|
||||||
|
<th scope="col" class="px-6 py-3">Ruangan</th>
|
||||||
|
<th scope="col" class="px-6 py-3 rounded-r-lg">Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="attendance-table-body">
|
||||||
|
@forelse ($absensi as $key => $item)
|
||||||
|
<tr class="bg-white border-b hover:bg-gray-50">
|
||||||
|
<td class="px-6 py-4 font-medium text-gray-900">{{ $key + 1 }}</td>
|
||||||
|
<td class="px-6 py-4">{{ $item->siswa->nama_siswa ?? 'Siswa Dihapus' }}</td>
|
||||||
|
<td class="px-6 py-4">{{ $item->siswa->jurusan->nama_jurusan ?? 'N/A' }}</td>
|
||||||
|
<td class="px-6 py-4">{{ $item->siswa->kelas->nama_kelas ?? 'N/A' }}</td>
|
||||||
|
<td class="px-6 py-4">{{ \Carbon\Carbon::parse($item->waktu)->format('H:i:s') }}</td>
|
||||||
|
<td class="px-6 py-4">{{ $item->devices->nama_device ?? 'Device Dihapus' }}</td>
|
||||||
|
<td class="px-6 py-4">
|
||||||
|
@if(strtolower($item->status) == 'hadir')
|
||||||
|
<span class="px-2 py-1 font-semibold leading-tight text-green-700 bg-green-100 rounded-full">Hadir</span>
|
||||||
|
@elseif(strtolower($item->status) == 'terlambat')
|
||||||
|
<span class="px-2 py-1 font-semibold leading-tight text-yellow-700 bg-yellow-100 rounded-full">Terlambat</span>
|
||||||
|
@else
|
||||||
|
<span class="px-2 py-1 font-semibold leading-tight text-red-700 bg-red-100 rounded-full">{{ ucfirst($item->status) }}</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr class="bg-white border-b">
|
||||||
|
<td colspan="7" class="px-6 py-4 text-center text-gray-500">
|
||||||
|
Tidak ada data yang cocok dengan filter yang dipilih.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- Kolom Kanan: Live Camera -->
|
||||||
|
<div class="lg:col-span-1">
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200 sticky top-6">
|
||||||
|
<h2 id="camera-title" class="text-xl font-semibold text-gray-700 mb-4 text-center">Live Camera Feed</h2>
|
||||||
|
<div id="cameraContainer" class="w-full aspect-video rounded-lg bg-gray-900 flex items-center justify-center overflow-hidden">
|
||||||
|
<img id="cameraFeed" src="" class="hidden w-full h-full object-cover">
|
||||||
|
<div id="cameraStatus" class="text-center text-gray-400 p-4">
|
||||||
|
<p class="flex items-center justify-center h-full">Pilih device untuk melihat live feed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tabel Presensi -->
|
|
||||||
<h5 class="text-xl font-semibold text-gray-800 mb-4">Hasil Presensi</h5>
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="w-full border-collapse border border-gray-200 shadow-md rounded-lg">
|
|
||||||
<thead class="bg-gray-100">
|
|
||||||
<tr class="text-left text-gray-700">
|
|
||||||
<th class="border px-4 py-2">No</th>
|
|
||||||
<th class="border px-4 py-2">Nama Siswa</th>
|
|
||||||
<th class="border px-4 py-2">Kelas</th>
|
|
||||||
<th class="border px-4 py-2">Waktu Presensi</th>
|
|
||||||
<th class="border px-4 py-2">Status</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="border hover:bg-gray-50">
|
|
||||||
<td class="border px-4 py-2">1</td>
|
|
||||||
<td class="border px-4 py-2">Ahmad Fauzi</td>
|
|
||||||
<td class="border px-4 py-2">Kelas 10</td>
|
|
||||||
<td class="border px-4 py-2">07:10 AM</td>
|
|
||||||
<td class="border px-4 py-2 text-green-600 font-semibold">Hadir</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="border hover:bg-gray-50">
|
|
||||||
<td class="border px-4 py-2">2</td>
|
|
||||||
<td class="border px-4 py-2">Siti Aisyah</td>
|
|
||||||
<td class="border px-4 py-2">Kelas 11</td>
|
|
||||||
<td class="border px-4 py-2">07:15 AM</td>
|
|
||||||
<td class="border px-4 py-2 text-green-600 font-semibold">Hadir</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="border hover:bg-gray-50">
|
|
||||||
<td class="border px-4 py-2">3</td>
|
|
||||||
<td class="border px-4 py-2">Budi Santoso</td>
|
|
||||||
<td class="border px-4 py-2">Kelas 12</td>
|
|
||||||
<td class="border px-4 py-2">07:30 AM</td>
|
|
||||||
<td class="border px-4 py-2 text-red-600 font-semibold">Terlambat</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
let cameraFeed = document.getElementById("cameraFeed");
|
const filterInputs = document.querySelectorAll('.filter-input');
|
||||||
let cameraStatus = document.getElementById("cameraStatus");
|
const filterForm = document.getElementById('filterForm');
|
||||||
|
|
||||||
cameraFeed.onload = function() {
|
// Otomatis submit form ketika nilai filter diubah
|
||||||
cameraFeed.classList.remove("hidden");
|
filterInputs.forEach(input => {
|
||||||
cameraStatus.style.display = "none"; // Sembunyikan teks jika kamera aktif
|
input.addEventListener('change', function() {
|
||||||
};
|
filterForm.submit();
|
||||||
|
|
||||||
cameraFeed.onerror = function() {
|
|
||||||
cameraFeed.style.display = "none";
|
|
||||||
cameraStatus.innerText = "Kamera Tidak Aktif";
|
|
||||||
};
|
|
||||||
// Event listener untuk dropdown kelas
|
|
||||||
document.getElementById("kelas").addEventListener("change", function() {
|
|
||||||
let kelas = this.value;
|
|
||||||
console.log("Filter kelas:", kelas);
|
|
||||||
// TODO: Filter tabel berdasarkan kelas
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event listener untuk filter tanggal
|
|
||||||
document.getElementById("tanggal").addEventListener("change", function() {
|
|
||||||
let tanggal = this.value;
|
|
||||||
console.log("Filter tanggal:", tanggal);
|
|
||||||
// TODO: Filter tabel berdasarkan tanggal
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
|
||||||
|
|
||||||
|
// Bagian untuk judul dan kamera
|
||||||
|
const laporanTitle = document.getElementById("laporan-title");
|
||||||
|
const tanggalInput = document.getElementById("tanggal");
|
||||||
|
const cameraFeed = document.getElementById("cameraFeed");
|
||||||
|
const cameraStatus = document.getElementById("cameraStatus");
|
||||||
|
const cameraTitle = document.getElementById("camera-title");
|
||||||
|
const deviceSelect = document.getElementById("device");
|
||||||
|
const devicesData = @json($devices);
|
||||||
|
|
||||||
|
function formatIndonesianDate(dateString) {
|
||||||
|
if (!dateString) return 'Hari Ini';
|
||||||
|
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
|
||||||
|
const months = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'];
|
||||||
|
const parts = dateString.match(/(\d{4})-(\d{2})-(\d{2})/);
|
||||||
|
if (!parts) return 'Hari Ini';
|
||||||
|
const date = new Date(parts[1], parts[2] - 1, parts[3]);
|
||||||
|
return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()} (${days[date.getDay()]})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDynamicElements() {
|
||||||
|
laporanTitle.innerText = `Laporan Kehadiran ${formatIndonesianDate(tanggalInput.value)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jalankan saat halaman pertama kali dimuat
|
||||||
|
updateDynamicElements();
|
||||||
|
|
||||||
|
// Sisa JavaScript untuk kamera...
|
||||||
|
// ... (Kode untuk kamera sama seperti sebelumnya) ...
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -3,24 +3,68 @@
|
||||||
@section('title', 'Smart School | Pendaftaran Siswa Baru')
|
@section('title', 'Smart School | Pendaftaran Siswa Baru')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8 relative">
|
||||||
|
|
||||||
|
<!-- Toast Notification Container -->
|
||||||
|
<div class="absolute top-0 right-0 p-4 space-y-2 z-50">
|
||||||
|
<!-- Toast for Validation Errors -->
|
||||||
|
@if ($errors->any())
|
||||||
|
<div id="toast-validation-error" class="flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg shadow-lg" role="alert">
|
||||||
|
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg">
|
||||||
|
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Error icon</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm font-normal">
|
||||||
|
<span class="mb-1 text-sm font-semibold text-gray-900">Data tidak valid!</span>
|
||||||
|
<ul class="mt-1.5 ml-1 list-disc list-inside">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8" data-dismiss-target="#toast-validation-error" aria-label="Close">
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Toast for General Errors -->
|
||||||
|
@if (session('error'))
|
||||||
|
<div id="toast-danger" class="flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow-lg" role="alert">
|
||||||
|
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg">
|
||||||
|
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Error icon</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm font-normal">{{ session('error') }}</div>
|
||||||
|
<button type="button" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8" data-dismiss-target="#toast-danger" aria-label="Close">
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Header Section -->
|
<!-- Header Section -->
|
||||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-8">
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-8">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-800 flex items-center">
|
<h1 class="text-2xl font-bold text-gray-800 flex items-center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 mr-3 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 mr-3 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
||||||
</svg>
|
|
||||||
Pendaftaran Siswa Baru
|
Pendaftaran Siswa Baru
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-gray-600 mt-2">Lengkapi formulir pendaftaran siswa dengan data yang valid</p>
|
<p class="text-gray-600 mt-2">Lengkapi formulir pendaftaran siswa dengan data yang valid</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 md:mt-0">
|
<div class="mt-4 md:mt-0">
|
||||||
<a href="{{ route('admin.siswa.index') }}"
|
<a href="{{ route('admin.siswa.index') }}" class="flex items-center px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition">
|
||||||
class="flex items-center px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
||||||
</svg>
|
|
||||||
Kembali ke Daftar Siswa
|
Kembali ke Daftar Siswa
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,190 +72,80 @@ class="flex items-center px-4 py-2 border border-gray-300 rounded-lg text-gray-7
|
||||||
|
|
||||||
<!-- Form Card -->
|
<!-- Form Card -->
|
||||||
<div class="bg-white rounded-xl shadow-md overflow-hidden">
|
<div class="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<!-- Form Progress -->
|
|
||||||
<div class="bg-gray-50 px-6 py-4 border-b border-gray-200">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="flex items-center text-indigo-600">
|
|
||||||
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-indigo-100">
|
|
||||||
<span class="text-sm font-medium">1</span>
|
|
||||||
</div>
|
|
||||||
<div class="ml-2 text-sm font-medium">Data Pribadi</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex-auto border-t-2 border-gray-200 mx-4"></div>
|
|
||||||
<div class="flex items-center text-gray-500">
|
|
||||||
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-100">
|
|
||||||
<span class="text-sm font-medium">2</span>
|
|
||||||
</div>
|
|
||||||
<div class="ml-2 text-sm font-medium">Data Akademik</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Form Content -->
|
|
||||||
<form id="createSiswaForm" action="{{ route('admin.siswa.store') }}" method="POST" enctype="multipart/form-data" class="p-6">
|
<form id="createSiswaForm" action="{{ route('admin.siswa.store') }}" method="POST" enctype="multipart/form-data" class="p-6">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<!-- Section 1: Data Pribadi -->
|
<!-- Section 1: Data Pribadi & Foto -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
Data Pribadi & Foto Wajah
|
||||||
</svg>
|
|
||||||
Data Pribadi
|
|
||||||
</h3>
|
</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div class="space-y-4">
|
||||||
<!-- Foto Profil -->
|
{{-- Nama, NISN, Tanggal Lahir, Jenis Kelamin --}}
|
||||||
<div class="md:col-span-1">
|
|
||||||
<div class="flex flex-col items-center">
|
|
||||||
<div class="relative mb-4">
|
|
||||||
<div id="imagePreview" class="w-40 h-40 rounded-lg bg-gray-100 border-2 border-dashed border-gray-300 overflow-hidden flex items-center justify-center">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<label for="foto_siswa" class="absolute bottom-0 right-0 bg-white p-2 rounded-full shadow-md cursor-pointer hover:bg-gray-50 transition">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
<input type="file" id="foto_siswa" name="foto_siswa" class="hidden" accept="image/*" required>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs text-gray-500 text-center">Format: JPG/PNG (Max 2MB)<br>Rasio 1:1 (persegi)</p>
|
|
||||||
@error('foto_siswa')
|
|
||||||
<p class="mt-1 text-xs text-red-600 text-center">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Data Pribadi -->
|
|
||||||
<div class="md:col-span-2 space-y-4">
|
|
||||||
<div>
|
<div>
|
||||||
<label for="nama_siswa" class="block text-sm font-medium text-gray-700 mb-1">Nama Lengkap <span class="text-red-500">*</span></label>
|
<label for="nama_siswa" class="block text-sm font-medium text-gray-700 mb-1">Nama Lengkap <span class="text-red-500">*</span></label>
|
||||||
<input type="text" id="nama_siswa" name="nama_siswa" required
|
<input type="text" id="nama_siswa" name="nama_siswa" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" value="{{ old('nama_siswa') }}" placeholder="Masukkan nama lengkap">
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
@error('nama_siswa') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
value="{{ old('nama_siswa') }}" placeholder="Masukkan nama lengkap">
|
|
||||||
@error('nama_siswa')
|
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<label for="nisn" class="block text-sm font-medium text-gray-700 mb-1">NISN <span class="text-red-500">*</span></label>
|
||||||
<div>
|
<input type="number" id="nisn" name="nisn" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" value="{{ old('nisn') }}" placeholder="Nomor Induk Siswa Nasional">
|
||||||
<label for="nisn" class="block text-sm font-medium text-gray-700 mb-1">NISN <span class="text-red-500">*</span></label>
|
@error('nisn') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
<input type="number" id="nisn" name="nisn" required
|
</div>
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
<div>
|
||||||
value="{{ old('nisn') }}" placeholder="Nomor Induk Siswa Nasional">
|
<label for="tanggal_lahir" class="block text-sm font-medium text-gray-700 mb-1">Tanggal Lahir <span class="text-red-500">*</span></label>
|
||||||
@error('nisn')
|
<input type="date" id="tanggal_lahir" name="tanggal_lahir" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" value="{{ old('tanggal_lahir') }}">
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
@error('tanggal_lahir') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="tanggal_lahir" class="block text-sm font-medium text-gray-700 mb-1">Tanggal Lahir <span class="text-red-500">*</span></label>
|
|
||||||
<input type="date" id="tanggal_lahir" name="tanggal_lahir" required
|
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
|
||||||
value="{{ old('tanggal_lahir') }}">
|
|
||||||
@error('tanggal_lahir')
|
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-1">Jenis Kelamin <span class="text-red-500">*</span></label>
|
<label class="block text-sm font-medium text-gray-700 mb-1">Jenis Kelamin <span class="text-red-500">*</span></label>
|
||||||
<div class="mt-1 flex space-x-4">
|
<div class="mt-2 flex space-x-4">
|
||||||
<label class="inline-flex items-center">
|
<label class="inline-flex items-center">
|
||||||
<input type="radio" name="jenis_kelamin" value="L" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300" required
|
<input type="radio" name="jenis_kelamin" value="L" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300" required {{ old('jenis_kelamin', 'L') == 'L' ? 'checked' : '' }}>
|
||||||
{{ old('jenis_kelamin', 'L') == 'L' ? 'checked' : '' }}>
|
|
||||||
<span class="ml-2 text-sm text-gray-700">Laki-laki</span>
|
<span class="ml-2 text-sm text-gray-700">Laki-laki</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="inline-flex items-center">
|
<label class="inline-flex items-center">
|
||||||
<input type="radio" name="jenis_kelamin" value="P" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300"
|
<input type="radio" name="jenis_kelamin" value="P" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300" {{ old('jenis_kelamin') == 'P' ? 'checked' : '' }}>
|
||||||
{{ old('jenis_kelamin') == 'P' ? 'checked' : '' }}>
|
|
||||||
<span class="ml-2 text-sm text-gray-700">Perempuan</span>
|
<span class="ml-2 text-sm text-gray-700">Perempuan</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@error('jenis_kelamin')
|
@error('jenis_kelamin') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 2: Data Kontak -->
|
|
||||||
<div class="mb-8">
|
|
||||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
Data Kontak
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email <span class="text-red-500">*</span></label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">Foto Wajah Siswa (Pilih 1-10 foto) <span class="text-red-500">*</span></label>
|
||||||
<input type="email" id="email" name="email" required
|
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
<div class="space-y-1 text-center">
|
||||||
value="{{ old('email') }}" placeholder="email@contoh.com">
|
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true"><path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||||
@error('email')
|
<div class="flex text-sm text-gray-600">
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
<label for="foto_siswa" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
|
||||||
@enderror
|
<span>Upload file</span>
|
||||||
</div>
|
<input id="foto_siswa" name="foto_siswa[]" type="file" class="sr-only" multiple required accept="image/jpeg,image/png,image/jpg">
|
||||||
|
</label>
|
||||||
<div>
|
<p class="pl-1">atau tarik dan lepas</p>
|
||||||
<label for="no_hp" class="block text-sm font-medium text-gray-700 mb-1">Nomor HP <span class="text-red-500">*</span></label>
|
</div>
|
||||||
<input type="tel" id="no_hp" name="no_hp" required
|
<p class="text-xs text-gray-500">PNG, JPG, JPEG hingga 2MB per file</p>
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
</div>
|
||||||
value="{{ old('no_hp') }}" placeholder="0812-3456-7890">
|
</div>
|
||||||
@error('no_hp')
|
@error('foto_siswa') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
@error('foto_siswa.*') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
@enderror
|
<div id="imagePreviewContainer" class="mt-4 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
|
||||||
<label for="alamat" class="block text-sm font-medium text-gray-700 mb-1">Alamat Lengkap <span class="text-red-500">*</span></label>
|
|
||||||
<textarea id="alamat" name="alamat" rows="3" required
|
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
|
||||||
placeholder="Jl. Contoh No. 123, Kota/Kabupaten">{{ old('alamat') }}</textarea>
|
|
||||||
@error('alamat')
|
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section 3: Data Akademik -->
|
<!-- Section 2: Informasi Akademik -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
Informasi Akademik
|
||||||
</svg>
|
|
||||||
Data Akademik
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
|
||||||
<label for="id_kelas" class="block text-sm font-medium text-gray-700 mb-1">Kelas <span class="text-red-500">*</span></label>
|
|
||||||
<select id="id_kelas" name="id_kelas" required
|
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
|
|
||||||
<option value="">-- Pilih Kelas --</option>
|
|
||||||
@foreach($kelas as $item)
|
|
||||||
<option value="{{ $item->id }}" {{ old('id_kelas') == $item->id ? 'selected' : '' }}>
|
|
||||||
{{ $item->nama_kelas }}
|
|
||||||
</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
@error('id_kelas')
|
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="id_jurusan" class="block text-sm font-medium text-gray-700 mb-1">Jurusan <span class="text-red-500">*</span></label>
|
<label for="id_jurusan" class="block text-sm font-medium text-gray-700 mb-1">Jurusan <span class="text-red-500">*</span></label>
|
||||||
<select id="id_jurusan" name="id_jurusan" required
|
<select id="id_jurusan" name="id_jurusan" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
|
|
||||||
<option value="">-- Pilih Jurusan --</option>
|
<option value="">-- Pilih Jurusan --</option>
|
||||||
@foreach($jurusan as $item)
|
@foreach($jurusan as $item)
|
||||||
<option value="{{ $item->id }}" {{ old('id_jurusan') == $item->id ? 'selected' : '' }}>
|
<option value="{{ $item->id }}" {{ old('id_jurusan') == $item->id ? 'selected' : '' }}>
|
||||||
|
@ -219,9 +153,44 @@ class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outlin
|
||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
@error('id_jurusan')
|
@error('id_jurusan') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
|
</div>
|
||||||
@enderror
|
<div>
|
||||||
|
<label for="id_kelas" class="block text-sm font-medium text-gray-700 mb-1">Kelas <span class="text-red-500">*</span></label>
|
||||||
|
<select id="id_kelas" name="id_kelas" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
|
||||||
|
<option value="">-- Pilih Kelas --</option>
|
||||||
|
@foreach($kelas as $item)
|
||||||
|
<option value="{{ $item->id }}" {{ old('id_kelas') == $item->id ? 'selected' : '' }}>
|
||||||
|
{{ $item->nama_kelas }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
@error('id_kelas') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Section 3: Informasi Kontak -->
|
||||||
|
<div class="mb-8">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg>
|
||||||
|
Informasi Kontak
|
||||||
|
</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email <span class="text-red-500">*</span></label>
|
||||||
|
<input type="email" id="email" name="email" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" value="{{ old('email') }}" placeholder="email@contoh.com">
|
||||||
|
@error('email') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="no_hp" class="block text-sm font-medium text-gray-700 mb-1">Nomor HP <span class="text-red-500">*</span></label>
|
||||||
|
<input type="tel" id="no_hp" name="no_hp" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" value="{{ old('no_hp') }}" placeholder="0812-3456-7890">
|
||||||
|
@error('no_hp') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<label for="alamat" class="block text-sm font-medium text-gray-700 mb-1">Alamat Lengkap <span class="text-red-500">*</span></label>
|
||||||
|
<textarea id="alamat" name="alamat" rows="3" required class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="Jl. Contoh No. 123, Kota/Kabupaten">{{ old('alamat') }}</textarea>
|
||||||
|
@error('alamat') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -232,9 +201,7 @@ class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outlin
|
||||||
Reset Form
|
Reset Form
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" /></svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
|
|
||||||
</svg>
|
|
||||||
Simpan Data Siswa
|
Simpan Data Siswa
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -243,19 +210,44 @@ class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outlin
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Preview image upload
|
// Script untuk menampilkan preview dari banyak gambar
|
||||||
document.getElementById('foto_siswa').addEventListener('change', function(e) {
|
document.getElementById('foto_siswa').addEventListener('change', function(e) {
|
||||||
const preview = document.getElementById('imagePreview');
|
const previewContainer = document.getElementById('imagePreviewContainer');
|
||||||
const file = e.target.files[0];
|
previewContainer.innerHTML = ''; // Bersihkan preview lama setiap kali ada perubahan
|
||||||
const reader = new FileReader();
|
|
||||||
|
|
||||||
reader.onload = function(e) {
|
if (this.files) {
|
||||||
preview.innerHTML = `<img src="${e.target.result}" class="w-full h-full object-cover" alt="Preview">`;
|
const filesToShow = Array.from(this.files).slice(0, 10);
|
||||||
}
|
filesToShow.forEach(file => {
|
||||||
|
const reader = new FileReader();
|
||||||
if (file) {
|
reader.onload = function(event) {
|
||||||
reader.readAsDataURL(file);
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'relative w-full h-32 rounded-lg overflow-hidden shadow-sm border';
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = event.target.result;
|
||||||
|
img.className = 'w-full h-full object-cover';
|
||||||
|
img.alt = file.name;
|
||||||
|
const caption = document.createElement('div');
|
||||||
|
caption.className = 'absolute bottom-0 left-0 right-0 bg-black bg-opacity-50 text-white text-xs p-1 truncate';
|
||||||
|
caption.textContent = file.name;
|
||||||
|
wrapper.appendChild(img);
|
||||||
|
wrapper.appendChild(caption);
|
||||||
|
previewContainer.appendChild(wrapper);
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Script untuk menghilangkan toast setelah beberapa detik
|
||||||
|
window.setTimeout(function() {
|
||||||
|
const toastElements = document.querySelectorAll('[id^="toast-"]');
|
||||||
|
toastElements.forEach(function(toast) {
|
||||||
|
const closeButton = toast.querySelector('[data-dismiss-target]');
|
||||||
|
if (closeButton) {
|
||||||
|
closeButton.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 5000); // Hilang setelah 5 detik
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
Loading…
Reference in New Issue