penialain pelatih.
This commit is contained in:
parent
9c68d4b734
commit
6307484ffe
|
|
@ -39,7 +39,7 @@ class MemberDashboardController extends Controller
|
|||
|
||||
// return view('anggota.dashboard', compact('anggota', 'jadwal', 'tagihan_spp', 'persentase_hadir'));
|
||||
// }
|
||||
public function index()
|
||||
public function index()
|
||||
{
|
||||
// 1. Ambil data profil anggota yang sedang login
|
||||
$user = Auth::user();
|
||||
|
|
@ -49,21 +49,22 @@ public function index()
|
|||
return redirect('/')->with('error', 'Data anggota tidak ditemukan.');
|
||||
}
|
||||
|
||||
// 2. Ambil Jadwal Latihan (Hanya yang sesuai dengan Kolat si anggota)
|
||||
// 2. Ambil Jadwal Latihan
|
||||
// PERBAIKAN: Gunakan today() atau now()->toDateString() alih-alih now().
|
||||
// now() membawa jam (misal 19:42), sehingga jadwal hari ini yang jamnya sudah lewat bisa tidak terbaca.
|
||||
$jadwal = Jadwal::where('kolat_id', $anggota->kolat_id)
|
||||
->where('tanggal', '>=', now())
|
||||
->where('tanggal', '>=', today())
|
||||
->orderBy('tanggal', 'asc')
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// 3. Ambil Tagihan SPP
|
||||
// PERBAIKAN: Menggunakan relasi spp() yang sudah Anda buat di model Anggota.
|
||||
// Asumsi di tabel SPP Anda terdapat kolom 'status' (misal: 'Pending' atau 'Belum Lunas')
|
||||
$tagihan_spp = $anggota->spp()->where('status', 'Pending')->get();
|
||||
// PERBAIKAN PENTING: Tadi kita sepakat status awalnya adalah 'belum_bayar', bukan 'Pending'
|
||||
$tagihan_spp = $anggota->spp()
|
||||
->where('status', 'belum_bayar')
|
||||
->get();
|
||||
|
||||
// 4. Hitung Ringkasan Kehadiran
|
||||
// Contoh mengambil total kehadiran dari relasi riwayatPresensi()
|
||||
// Ini asumsikan Anda memiliki kolom 'status_hadir' (misal: 'Hadir') di tabel Presensi
|
||||
$total_latihan = $anggota->riwayatPresensi()->count();
|
||||
$total_hadir = $anggota->riwayatPresensi()->where('status', 'Hadir')->count();
|
||||
|
||||
|
|
@ -74,4 +75,4 @@ public function index()
|
|||
|
||||
return view('anggota.dashboard', compact('anggota', 'jadwal', 'tagihan_spp', 'persentase_hadir'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
use App\Models\Anggota;
|
||||
use App\Models\Penilaian;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PenilaianController extends Controller
|
||||
{
|
||||
|
|
@ -24,7 +25,25 @@ public function index()
|
|||
|
||||
public function create($id)
|
||||
{
|
||||
// BYPASS: Hapus pengecekan auth() agar tidak mental ke login
|
||||
// 1. Tentukan ID Anggota Penilai (Lebih aman pakai operator ?? untuk mencegah error jika relasi anggota kosong)
|
||||
$anggota_penilai_id = auth()->check() ? (auth()->user()->anggota->id ?? 1) : 1;
|
||||
|
||||
// 2. Tentukan rentang waktu bulan berjalan
|
||||
$awalBulanIni = Carbon::now()->startOfMonth();
|
||||
$akhirBulanIni = Carbon::now()->endOfMonth();
|
||||
|
||||
// 3. PENJAGA AWAL: Cek sebelum halaman form kuis terbuka
|
||||
$sudahMenilai = Penilaian::where('anggota_id', $anggota_penilai_id)
|
||||
->where('pelatih_id', $id)
|
||||
->whereBetween('bulan_evaluasi', [$awalBulanIni, $akhirBulanIni])
|
||||
->exists();
|
||||
|
||||
if ($sudahMenilai) {
|
||||
// Jika sudah pernah menilai bulan ini, langsung tendang balik ke halaman daftar pelatih
|
||||
return redirect()->route('penilaian.anggota_index')
|
||||
->with('error', 'Anda sudah memberikan penilaian untuk pelatih ini pada bulan ini!');
|
||||
}
|
||||
|
||||
$pelatih = Anggota::with('kolat')->findOrFail($id);
|
||||
return view('penilaian.create', compact('pelatih'));
|
||||
}
|
||||
|
|
@ -42,10 +61,25 @@ public function store(Request $request)
|
|||
'kritik_saran' => 'nullable|string'
|
||||
]);
|
||||
|
||||
$anggota_penilai_id = auth()->check() ? (auth()->user()->anggota->id ?? 1) : 1;
|
||||
$awalBulanIni = Carbon::now()->startOfMonth();
|
||||
$akhirBulanIni = Carbon::now()->endOfMonth();
|
||||
|
||||
// 4. PENJAGA KEDUA: Memastikan keamanan data saat disubmit
|
||||
$sudahMenilai = Penilaian::where('anggota_id', $anggota_penilai_id)
|
||||
->where('pelatih_id', $request->pelatih_id)
|
||||
->whereBetween('bulan_evaluasi', [$awalBulanIni, $akhirBulanIni])
|
||||
->exists();
|
||||
|
||||
if ($sudahMenilai) {
|
||||
return redirect()->route('penilaian.anggota_index')
|
||||
->with('error', 'Gagal! Anda sudah menilai pelatih ini bulan ini.');
|
||||
}
|
||||
|
||||
Penilaian::create([
|
||||
'pelatih_id' => $request->pelatih_id,
|
||||
'anggota_id' => 1, // TESTING: Isi manual ID Anggota penilai (misal ID 1)
|
||||
'bulan_evaluasi' => Carbon::now()->startOfMonth(),
|
||||
'anggota_id' => $anggota_penilai_id,
|
||||
'bulan_evaluasi' => $awalBulanIni,
|
||||
'metode_pelatihan' => $request->metode_pelatihan,
|
||||
'komunikasi' => $request->komunikasi,
|
||||
'sikap_kepribadian' => $request->sikap_kepribadian,
|
||||
|
|
@ -55,35 +89,36 @@ public function store(Request $request)
|
|||
'kritik_saran' => $request->kritik_saran,
|
||||
]);
|
||||
|
||||
return redirect()->route('penilaian.index')->with('success', 'Penilaian berhasil disimpan!');
|
||||
return redirect()->route('penilaian.anggota_index')->with('success', 'Penilaian pelatih berhasil disimpan!');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$pelatih = Anggota::with('kolat')->findOrFail($id);
|
||||
// FUNGSI INI KEMBALI DITAMBAHKAN KARENA SEMPAT TERHAPUS
|
||||
public function show($id)
|
||||
{
|
||||
$pelatih = Anggota::with('kolat')->findOrFail($id);
|
||||
|
||||
// Hitung rata-rata tiap kriteria bulan ini
|
||||
$rekap = Penilaian::where('pelatih_id', $id)
|
||||
->whereMonth('bulan_evaluasi', now()->month)
|
||||
->selectRaw('
|
||||
AVG(metode_pelatihan) as avg_metode,
|
||||
AVG(komunikasi) as avg_komunikasi,
|
||||
AVG(sikap_kepribadian) as avg_sikap,
|
||||
AVG(kepemimpinan) as avg_kepemimpinan,
|
||||
AVG(konsistensi_komitmen) as avg_komitmen,
|
||||
AVG(kedekatan_interpersonal) as avg_interpersonal
|
||||
')->first();
|
||||
// Hitung rata-rata tiap kriteria bulan ini
|
||||
$rekap = Penilaian::where('pelatih_id', $id)
|
||||
->whereMonth('bulan_evaluasi', now()->month)
|
||||
->selectRaw('
|
||||
AVG(metode_pelatihan) as avg_metode,
|
||||
AVG(komunikasi) as avg_komunikasi,
|
||||
AVG(sikap_kepribadian) as avg_sikap,
|
||||
AVG(kepemimpinan) as avg_kepemimpinan,
|
||||
AVG(konsistensi_komitmen) as avg_komitmen,
|
||||
AVG(kedekatan_interpersonal) as avg_interpersonal
|
||||
')->first();
|
||||
|
||||
// Ambil ulasan/kritik saran (Pluck agar anonim)
|
||||
$ulasan = Penilaian::where('pelatih_id', $id)
|
||||
->whereMonth('bulan_evaluasi', now()->month)
|
||||
->whereNotNull('kritik_saran')
|
||||
->pluck('kritik_saran');
|
||||
// Ambil ulasan/kritik saran (Pluck agar anonim)
|
||||
$ulasan = Penilaian::where('pelatih_id', $id)
|
||||
->whereMonth('bulan_evaluasi', now()->month)
|
||||
->whereNotNull('kritik_saran')
|
||||
->pluck('kritik_saran');
|
||||
|
||||
return view('penilaian.rekap', compact('pelatih', 'rekap', 'ulasan'));
|
||||
}
|
||||
return view('penilaian.rekap', compact('pelatih', 'rekap', 'ulasan'));
|
||||
}
|
||||
|
||||
public function exportPdf($id)
|
||||
public function exportPdf($id)
|
||||
{
|
||||
// 1. Ambil data pelatih
|
||||
$pelatih = Anggota::with('kolat')->findOrFail($id);
|
||||
|
|
@ -124,5 +159,4 @@ public function indexAnggota()
|
|||
|
||||
return view('penilaian.anggota_index', compact('data_pelatih'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ class="rounded-circle shadow-sm border border-3 border-white" width="100" alt="F
|
|||
<div class="card card-custom h-100 border-0">
|
||||
<div class="card-header bg-white border-0 pt-4 pb-0 px-4 d-flex align-items-center justify-content-between">
|
||||
<h5 class="fw-bold text-dark mb-0">Jadwal Latihan Terdekat</h5>
|
||||
<a href="#" class="btn btn-sm btn-light text-primary fw-semibold rounded-pill px-3">Lihat Semua</a>
|
||||
<a href="{{ route('presensi.anggota.index') }}" class="btn btn-sm btn-light text-primary fw-semibold rounded-pill px-3">Lihat Semua</a>
|
||||
</div>
|
||||
<div class="card-body px-4 pt-3 pb-4 table-responsive">
|
||||
<table class="table table-modern table-borderless align-middle mb-0">
|
||||
|
|
|
|||
|
|
@ -130,4 +130,29 @@
|
|||
font-size: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
@if(session('error'))
|
||||
Swal.fire({
|
||||
title: 'Tidak Bisa Menilai!',
|
||||
text: "{{ session('error') }}",
|
||||
icon: 'warning',
|
||||
confirmButtonColor: '#f59e0b',
|
||||
confirmButtonText: 'Mengerti'
|
||||
});
|
||||
@endif
|
||||
|
||||
@if(session('success'))
|
||||
Swal.fire({
|
||||
title: 'Berhasil!',
|
||||
text: "{{ session('success') }}",
|
||||
icon: 'success',
|
||||
confirmButtonColor: '#10b981',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
@endif
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
|
|||
|
|
@ -1,218 +1,3 @@
|
|||
{{-- @extends('layout.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Menggunakan col-12 agar card membentang penuh (Full Width) -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
||||
<!-- Tombol Kembali -->
|
||||
<a href="{{ route('penilaian.anggota_index') }}" class="text-decoration-none text-muted mb-3 d-inline-block">
|
||||
<i class="bi bi-arrow-left me-1"></i> Kembali ke Daftar Pelatih
|
||||
</a>
|
||||
|
||||
<div class="card border-0 shadow-sm form-penilaian-card">
|
||||
<!-- Header Card Friendly -->
|
||||
<div class="card-header text-center border-0 pt-5 pb-4 bg-white" style="border-radius: 20px 20px 0 0;">
|
||||
<div class="pelatih-avatar mb-3 mx-auto">
|
||||
@if($pelatih->foto_profil)
|
||||
<img src="{{ asset('storage/' . $pelatih->foto_profil) }}" alt="Foto">
|
||||
@else
|
||||
<i class="bi bi-person-fill"></i>
|
||||
@endif
|
||||
</div>
|
||||
<h4 class="fw-bold mb-1">Nilai Kak <span style="color: var(--accent-color);">{{ $pelatih->nama_lengkap }}</span></h4>
|
||||
<span class="badge bg-light text-dark px-3 py-2 rounded-pill mt-2">
|
||||
<i class="bi bi-geo-alt-fill text-danger me-1"></i> Kolat: {{ $pelatih->kolat->nama_kolat ?? '-' }}
|
||||
</span>
|
||||
<p class="text-muted small mt-3 px-3 mx-auto" style="max-width: 600px;">
|
||||
Yuk, berikan penilaian jujurmu untuk evaluasi bulan {{ now()->translatedFormat('F Y') }}. Tenang saja, namamu akan dirahasiakan! 🤫
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-4 p-lg-5 pt-2">
|
||||
<form action="{{ route('penilaian.store') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="pelatih_id" value="{{ $pelatih->id }}">
|
||||
|
||||
@php
|
||||
$kriterias = [
|
||||
'metode_pelatihan' => 'Cara Menyampaikan Materi',
|
||||
'komunikasi' => 'Cara Berkomunikasi dengan Anggota',
|
||||
'sikap_kepribadian' => 'Sikap & Kedisiplinan Pelatih',
|
||||
'kepemimpinan' => 'Jiwa Kepemimpinan di Lapangan',
|
||||
'konsistensi_komitmen' => 'Kehadiran & Semangat Melatih',
|
||||
'kedekatan_interpersonal' => 'Kedekatan & Kepedulian'
|
||||
];
|
||||
|
||||
// Emoji untuk masing-masing nilai 1-5
|
||||
$emojis = [
|
||||
1 => '😞',
|
||||
2 => '😐',
|
||||
3 => '🙂',
|
||||
4 => '😄',
|
||||
5 => '🤩'
|
||||
];
|
||||
@endphp
|
||||
|
||||
<!-- Dibikin 2 Kolom untuk Layar Besar agar tidak terlalu kosong saat Full Width -->
|
||||
<div class="row g-4">
|
||||
@foreach($kriterias as $key => $label)
|
||||
<div class="col-md-6">
|
||||
<div class="kriteria-box h-100 p-4 rounded-4 bg-light">
|
||||
<label class="fw-bold d-block mb-4 text-dark text-center fs-6">{{ $label }}</label>
|
||||
|
||||
<div class="d-flex justify-content-between gap-2 gap-md-3">
|
||||
@foreach(range(1, 5) as $score)
|
||||
<div class="rating-wrapper flex-fill">
|
||||
<!-- Input Radio disembunyikan -->
|
||||
<input class="rating-input" type="radio" name="{{ $key }}" id="{{ $key.$score }}" value="{{ $score }}" required>
|
||||
<!-- Label dibuat seperti tombol kotak -->
|
||||
<label class="rating-label w-100" for="{{ $key.$score }}">
|
||||
<span class="emoji-rate">{{ $emojis[$score] }}</span>
|
||||
<span class="score-text">{{ $score }}</span>
|
||||
</label>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
<!-- Keterangan text di bawah angka (Hanya tampil 1 dan 5) -->
|
||||
<div class="d-flex justify-content-between mt-3 px-2 text-muted fw-medium" style="font-size: 0.75rem;">
|
||||
<span>Kurang</span>
|
||||
<span>Sangat Baik</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mb-4 mt-5">
|
||||
<label class="fw-bold mb-3 text-dark fs-5">Pesan, Kritik & Saran 📝</label>
|
||||
<textarea name="kritik_saran" class="form-control bg-light border-0" rows="5" placeholder="Tulis masukan, pujian, atau saranmu di sini (boleh dikosongi)..." style="border-radius: 15px; padding: 20px; resize: none;"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-5">
|
||||
<button type="submit" class="btn btn-submit-nilai py-3 fw-bold fs-5">
|
||||
<i class="bi bi-send-fill me-2"></i> Kirim Penilaian Rahasia
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Styling Card Utama */
|
||||
.form-penilaian-card {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Foto Profil Kecil di Atas */
|
||||
.pelatih-avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3.5rem;
|
||||
color: var(--dark-blue, #06163a);
|
||||
border: 5px solid #fff;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
.pelatih-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Menyembunyikan radio button asli */
|
||||
.rating-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Kotak Pilihan Angka & Emoji */
|
||||
.rating-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px 5px;
|
||||
background: #ffffff;
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.emoji-rate {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 5px;
|
||||
filter: grayscale(100%); /* Bikin abu-abu dulu kalau belum dipilih */
|
||||
opacity: 0.6;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.score-text {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Efek Saat Di-Hover (Sentuh) */
|
||||
.rating-label:hover {
|
||||
background: #f1f5f9;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
.rating-label:hover .emoji-rate {
|
||||
filter: grayscale(0%);
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Efek Saat Di-Klik (Dipilih) */
|
||||
.rating-input:checked + .rating-label {
|
||||
background: var(--accent-color, #feb900);
|
||||
border-color: var(--accent-color, #feb900);
|
||||
color: var(--dark-blue, #06163a);
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 20px rgba(254, 185, 0, 0.3);
|
||||
}
|
||||
|
||||
.rating-input:checked + .rating-label .emoji-rate {
|
||||
filter: grayscale(0%);
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* Input Textarea Focus */
|
||||
textarea.form-control:focus {
|
||||
background: #ffffff !important;
|
||||
border: 2px solid var(--accent-color, #feb900) !important;
|
||||
box-shadow: 0 0 0 0.25rem rgba(254, 185, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Tombol Submit */
|
||||
.btn-submit-nilai {
|
||||
background: var(--dark-blue, #06163a);
|
||||
color: #fff;
|
||||
border-radius: 15px;
|
||||
border: none;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn-submit-nilai:hover {
|
||||
background: var(--accent-color, #feb900);
|
||||
color: var(--dark-blue, #06163a);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(254, 185, 0, 0.4);
|
||||
}
|
||||
</style>
|
||||
@endsection --}}
|
||||
|
||||
@extends('layout.app')
|
||||
|
||||
@section('content')
|
||||
|
|
@ -252,8 +37,8 @@
|
|||
</div>
|
||||
@endif
|
||||
<div class="text-start">
|
||||
<div class="fw-bold text-dark lh-1" style="font-size: 0.9rem;">Kak {{ $pelatih->nama_lengkap }}</div>
|
||||
<div class="text-muted" style="font-size: 0.75rem;">Kolat {{ $pelatih->kolat->nama_kolat ?? '-' }}</div>
|
||||
<div class="fw-bold text-dark lh-1" style="font-size: 0.9rem;"> {{ $pelatih->nama_lengkap }}</div>
|
||||
<div class="text-muted" style="font-size: 0.75rem;">{{ $pelatih->kolat->nama_kolat ?? '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -263,7 +48,7 @@
|
|||
|
||||
@php
|
||||
$kriterias = [
|
||||
'metode_pelatihan' => 'Bagaimana cara Kakak Pelatih menyampaikan materi hari ini?',
|
||||
'metode_pelatihan' => 'Bagaimana cara Pelatih menyampaikan materi hari ini?',
|
||||
'komunikasi' => 'Seberapa asik dan jelas komunikasinya dengan anggota?',
|
||||
'sikap_kepribadian' => 'Bagaimana sikap dan kedisiplinan pelatih di lapangan?',
|
||||
'kepemimpinan' => 'Seberapa baik jiwa kepemimpinan pelatih saat memandu latihan?',
|
||||
|
|
@ -315,7 +100,7 @@
|
|||
<div class="mb-4">
|
||||
<span class="emoji-rate d-inline-block mb-3" style="font-size: 4rem; filter: none; opacity: 1;">📝</span>
|
||||
<h3 class="fw-bolder text-dark mb-2">Satu langkah lagi!</h3>
|
||||
<p class="text-muted">Ada pesan, kritik, saran, atau pujian untuk Kak {{ $pelatih->nama_lengkap }}? (Boleh dikosongkan kok)</p>
|
||||
<p class="text-muted">Ada pesan, kritik, saran, atau pujian untuk {{ $pelatih->nama_lengkap }}? (Boleh dikosongkan kok)</p>
|
||||
</div>
|
||||
|
||||
<textarea name="kritik_saran" class="form-control bg-light border-0 mb-4 shadow-inner" rows="5" placeholder="Ketik pesan rahasiamu di sini..." style="border-radius: 20px; padding: 25px; resize: none; font-size: 1.1rem;"></textarea>
|
||||
|
|
|
|||
Loading…
Reference in New Issue