Revisi data dan validasi

This commit is contained in:
WahyuTegarP 2026-04-04 13:00:28 +07:00
parent 243bb7cb49
commit ccdd44927b
10 changed files with 145 additions and 24 deletions

View File

@ -5,6 +5,8 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use App\Models\Biodata;
use Carbon\Carbon;
class AdminController extends Controller class AdminController extends Controller
{ {
@ -36,18 +38,63 @@ public function authenticate(Request $request)
} }
public function dashboard() public function dashboard()
{ {
if (!Auth::check()) { // total diagnosis
return redirect()->route('admin.login'); $totalDiagnosis = Biodata::count();
}
// Get statistics // hari ini
$stats = $this->getStatistics(); $todayDiagnosis = Biodata::whereDate('created_at', Carbon::today())->count();
return view('admin.dashboard', [ // total user
'stats' => $stats $totalUsers = Biodata::count();
]);
} // penyakit paling umum
$mostCommon = Biodata::select('hasil_diagnosis')
->whereNotNull('hasil_diagnosis')
->groupBy('hasil_diagnosis')
->orderByRaw('COUNT(*) DESC')
->value('hasil_diagnosis');
// diagnosis terbaru
$recent = Biodata::select('hasil_diagnosis', 'created_at')
->latest()
->take(5)
->get();
// format tabel
$recentFormatted = $recent->map(function ($item) {
return [
'date' => $item->created_at,
'disease' => $item->hasil_diagnosis,
'count' => 1
];
});
// 🔥 CHART (HARUS DI LUAR MAP)
$diseaseStats = Biodata::select('hasil_diagnosis')
->whereNotNull('hasil_diagnosis')
->get()
->groupBy('hasil_diagnosis')
->map(function ($item) {
return count($item);
});
$chartLabels = $diseaseStats->keys()->values();
$chartData = $diseaseStats->values();
// kirim ke blade
$stats = [
'total_diagnosis' => $totalDiagnosis,
'today_diagnosis' => $todayDiagnosis,
'total_users' => $totalUsers,
'most_common_disease' => $mostCommon,
'recent_diagnosis' => $recentFormatted,
'chart_labels' => $chartLabels,
'chart_data' => $chartData
];
return view('admin.dashboard', compact('stats'));
}
public function logout(Request $request) public function logout(Request $request)
{ {

View File

@ -14,7 +14,7 @@ public function prosesDiagnosis(Request $request)
// validasi minimal 3 gejala // validasi minimal 3 gejala
if (count($input) < 3) { if (count($input) < 3) {
return redirect()->route('gejala') return redirect()->route('gejala')
->with('error', 'Pilih minimal 3 gejala!'); ->with('error', 'Pilih minimal 5 dan maksimal 7 gejala!');
} }
$inputNama = $input; $inputNama = $input;

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('biodata', function (Blueprint $table) {
//
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('biodata', function (Blueprint $table) {
//
});
}
};

Binary file not shown.

View File

@ -8,7 +8,7 @@ app = Flask(__name__)
# ========================= # =========================
# LOAD MODEL # LOAD MODEL
# ========================= # =========================
model = joblib.load("../python_artifacts/modell.joblib") model = joblib.load("../python_artifacts/model.joblib")
# ========================= # =========================
# LOAD FEATURE # LOAD FEATURE

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -141,11 +141,10 @@
} }
/* ===== STATS GRID ===== */ /* ===== STATS GRID ===== */
.stats-grid{ .stats-grid {
display:grid; display: grid;
grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(4, 1fr);
gap:24px; gap: 20px;
margin-bottom:40px;
} }
.stat-card{ .stat-card{
@ -342,6 +341,14 @@
<!-- Statistics --> <!-- Statistics -->
<div class="stats-grid"> <div class="stats-grid">
</div>
<div id="chartBox" style="display:none; margin-top:20px;">
<div class="data-section">
<div class="section-title">📊 Statistik Penyakit</div>
<canvas id="chartPenyakit"></canvas>
</div>
</div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-header"> <div class="stat-header">
<div> <div>
@ -350,7 +357,7 @@
</div> </div>
<div class="stat-icon">📊</div> <div class="stat-icon">📊</div>
</div> </div>
<div class="stat-change">+12 hari ini</div> <div class="stat-change">+{{ $stats['today_diagnosis'] }} hari ini</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
@ -375,7 +382,7 @@
<div class="stat-change">Pengguna aktif</div> <div class="stat-change">Pengguna aktif</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="toggleChart()" style="cursor:pointer;">
<div class="stat-header"> <div class="stat-header">
<div> <div>
<div class="stat-value" style="font-size:24px;">{{ $stats['most_common_disease'] }}</div> <div class="stat-value" style="font-size:24px;">{{ $stats['most_common_disease'] }}</div>
@ -436,6 +443,32 @@
</div> </div>
@include('components.scroll-top') @include('components.scroll-top')
<script>
function toggleChart() {
const chartBox = document.getElementById('chartBox');
if (chartBox.style.display === "none") {
chartBox.style.display = "block";
} else {
chartBox.style.display = "none";
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('chartPenyakit');
new Chart(ctx, {
type: 'bar',
data: {
labels: {!! json_encode($stats['chart_labels']) !!},
datasets: [{
label: 'Jumlah Kasus',
data: {!! json_encode($stats['chart_data']) !!}
}]
}
});
</script>
</body> </body>
</html> </html>

View File

@ -780,13 +780,13 @@
<!-- FORM CARD --> <!-- FORM CARD -->
<div class="form-card"> <div class="form-card">
<form action="{{ route('diagnosis.proses') }}" method="POST"> <form id="gejalaForm" action="{{ route('diagnosis.proses') }}" method="POST">
@csrf @csrf
<!-- Info Box --> <!-- Info Box -->
<div class="info-box"> <div class="info-box">
<div class="icon">💡</div> <div class="icon">💡</div>
<p>Pilih semua gejala yang Anda amati pada kucing Anda. Semakin banyak gejala yang dipilih, semakin akurat diagnosis yang akan diberikan.</p> <p >Pilih minimal 4 dan maksimal 7 gejala yang terjadi pada kucing anda</p>
</div> </div>
<!-- Gejala Section --> <!-- Gejala Section -->
@ -848,7 +848,6 @@ class="gejala-checkbox"
const selectedCount = document.getElementById('selectedCount'); const selectedCount = document.getElementById('selectedCount');
const form = document.getElementById('gejalaForm'); const form = document.getElementById('gejalaForm');
// Update selected count
function updateSelectedCount() { function updateSelectedCount() {
const checked = document.querySelectorAll('.gejala-checkbox:checked').length; const checked = document.querySelectorAll('.gejala-checkbox:checked').length;
selectedCount.textContent = checked + ' dipilih'; selectedCount.textContent = checked + ' dipilih';
@ -858,7 +857,7 @@ function updateSelectedCount() {
setTimeout(() => selectedCount.classList.remove('animate'), 500); setTimeout(() => selectedCount.classList.remove('animate'), 500);
// Enable/disable submit button // Enable/disable submit button
if (checked > 0) { if (checked >= 4 && checked <= 7) {
submitBtn.disabled = false; submitBtn.disabled = false;
submitBtn.style.opacity = '1'; submitBtn.style.opacity = '1';
} else { } else {
@ -874,7 +873,21 @@ function updateSelectedCount() {
// Form submission // Form submission
form.addEventListener('submit', function(e) {
const checked = document.querySelectorAll('.gejala-checkbox:checked').length;
if (checked < 5) {
e.preventDefault();
alert("Minimal pilih 4 gejala!");
return;
}
if (checked > 7) {
e.preventDefault();
alert("Maksimal hanya 7 gejala!");
return;
}
});
// Search functionality // Search functionality
const searchInput = document.getElementById('searchGejala'); const searchInput = document.getElementById('searchGejala');