admin-gejala dan rule basis done

This commit is contained in:
E31232303evimr 2026-02-25 03:46:16 +07:00
parent 9d860df6e8
commit 86c6a51401
15 changed files with 1101 additions and 56 deletions

View File

@ -13,7 +13,7 @@ class GejalaController extends Controller
*/
public function index()
{
$gejalas = MasterGejala::latest()->paginate(10);
$gejalas = MasterGejala::orderByRaw('LENGTH(id_gejala) ASC, id_gejala ASC')->paginate(10);
return view('admin.gejala.index', compact('gejalas'));
}
@ -31,11 +31,11 @@ public function create()
public function store(Request $request)
{
$validated = $request->validate([
'kode_gejala' => ['required', 'string', 'max:10', 'unique:gejala,kode_gejala'],
'id_gejala' => ['required', 'string', 'max:10', 'unique:master_gejala,id_gejala'],
'nama_gejala' => ['required', 'string', 'max:255'],
], [
'kode_gejala.unique' => 'Kode gejala sudah digunakan',
'kode_gejala.required' => 'Kode gejala wajib diisi',
'id_gejala.required' => 'Kode gejala wajib diisi',
'id_gejala.unique' => 'Kode gejala sudah digunakan',
'nama_gejala.required' => 'Nama gejala wajib diisi',
]);
@ -46,15 +46,7 @@ public function store(Request $request)
}
/**
* Display the specified gejala.
*/
public function show(MasterGejala $gejala)
{
return view('admin.gejala.show', compact('gejala'));
}
/**
* Show the form for editing gejala.
* Show the form for editing the specified gejala.
*/
public function edit(MasterGejala $gejala)
{
@ -67,10 +59,9 @@ public function edit(MasterGejala $gejala)
public function update(Request $request, MasterGejala $gejala)
{
$validated = $request->validate([
'kode_gejala' => ['required', 'string', 'max:10', 'unique:gejala,kode_gejala,' . $gejala->id],
'nama_gejala' => ['required', 'string', 'max:255'],
], [
'kode_gejala.unique' => 'Kode gejala sudah digunakan',
'nama_gejala.required' => 'Nama gejala wajib diisi',
]);
$gejala->update($validated);

View File

@ -0,0 +1,157 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\RuleBasis;
use App\Models\MasterPenyakit;
use App\Models\MasterGejala;
use Illuminate\Http\Request;
class RuleBasisController extends Controller
{
/**
* Display a listing of rule basis.
*/
public function index(Request $request)
{
$query = RuleBasis::with(['penyakit', 'gejala']);
// Filter by penyakit
if ($request->filled('penyakit')) {
$query->where('id_penyakit', $request->penyakit);
}
// Filter by gejala
if ($request->filled('gejala')) {
$query->where('id_gejala', $request->gejala);
}
// Search by keterangan
if ($request->filled('search')) {
$query->where('keterangan', 'like', '%' . $request->search . '%');
}
$rules = $query->orderBy('id_penyakit', 'asc')
->orderBy('id_gejala', 'asc')
->paginate(15);
// Statistics
$totalRules = RuleBasis::count();
$avgCF = RuleBasis::avg('cf_pakar');
$highConfidenceRules = RuleBasis::where('cf_pakar', '>=', 0.7)->count();
$totalPenyakitWithRules = RuleBasis::distinct('id_penyakit')->count('id_penyakit');
// Data for filter dropdowns
$penyakits = MasterPenyakit::orderByRaw('LENGTH(id_penyakit) ASC, id_penyakit ASC')->get();
$gejalas = MasterGejala::orderByRaw('LENGTH(id_gejala) ASC, id_gejala ASC')->get();
return view('admin.rule-basis.index', compact(
'rules',
'penyakits',
'gejalas',
'totalRules',
'avgCF',
'highConfidenceRules',
'totalPenyakitWithRules'
));
}
/**
* Show the form for creating a new rule.
*/
public function create()
{
$penyakits = MasterPenyakit::orderByRaw('LENGTH(id_penyakit) ASC, id_penyakit ASC')->get();
$gejalas = MasterGejala::orderByRaw('LENGTH(id_gejala) ASC, id_gejala ASC')->get();
return view('admin.rule-basis.create', compact('penyakits', 'gejalas'));
}
/**
* Store a newly created rule.
*/
public function store(Request $request)
{
$validated = $request->validate([
'id_penyakit' => ['required', 'exists:master_penyakit,id_penyakit'],
'id_gejala' => ['required', 'exists:master_gejala,id_gejala'],
'mb' => ['required', 'numeric', 'min:0', 'max:1'],
'md' => ['required', 'numeric', 'min:0', 'max:1'],
'keterangan' => ['nullable', 'string'],
], [
'id_penyakit.required' => 'Penyakit wajib dipilih',
'id_penyakit.exists' => 'Penyakit tidak valid',
'id_gejala.required' => 'Gejala wajib dipilih',
'id_gejala.exists' => 'Gejala tidak valid',
'mb.required' => 'Nilai MB wajib diisi',
'mb.min' => 'Nilai MB minimal 0',
'mb.max' => 'Nilai MB maksimal 1',
'md.required' => 'Nilai MD wajib diisi',
'md.min' => 'Nilai MD minimal 0',
'md.max' => 'Nilai MD maksimal 1',
]);
// Check if rule already exists
$exists = RuleBasis::where('id_penyakit', $validated['id_penyakit'])
->where('id_gejala', $validated['id_gejala'])
->exists();
if ($exists) {
return back()->withInput()->withErrors([
'id_gejala' => 'Rule untuk penyakit dan gejala ini sudah ada!'
]);
}
RuleBasis::create($validated);
return redirect()->route('admin.rule-basis.index')
->with('success', 'Rule basis berhasil ditambahkan!');
}
/**
* Show the form for editing the specified rule.
*/
public function edit(RuleBasis $ruleBasis)
{
$penyakits = MasterPenyakit::orderByRaw('LENGTH(id_penyakit) ASC, id_penyakit ASC')->get();
$gejalas = MasterGejala::orderByRaw('LENGTH(id_gejala) ASC, id_gejala ASC')->get();
return view('admin.rule-basis.edit', compact('ruleBasis', 'penyakits', 'gejalas'));
}
/**
* Update the specified rule.
*/
public function update(Request $request, RuleBasis $ruleBasis)
{
$validated = $request->validate([
'mb' => ['required', 'numeric', 'min:0', 'max:1'],
'md' => ['required', 'numeric', 'min:0', 'max:1'],
'keterangan' => ['nullable', 'string'],
], [
'mb.required' => 'Nilai MB wajib diisi',
'mb.min' => 'Nilai MB minimal 0',
'mb.max' => 'Nilai MB maksimal 1',
'md.required' => 'Nilai MD wajib diisi',
'md.min' => 'Nilai MD minimal 0',
'md.max' => 'Nilai MD maksimal 1',
]);
$ruleBasis->update($validated);
return redirect()->route('admin.rule-basis.index')
->with('success', 'Rule basis berhasil diupdate!');
}
/**
* Remove the specified rule.
*/
public function destroy(RuleBasis $ruleBasis)
{
$ruleBasis->delete();
return redirect()->route('admin.rule-basis.index')
->with('success', 'Rule basis berhasil dihapus!');
}
}

View File

@ -2,23 +2,46 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MasterGejala extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'master_gejala';
/**
* The primary key associated with the table.
*/
protected $primaryKey = 'id_gejala';
/**
* Indicates if the model's ID is auto-incrementing.
*/
public $incrementing = false;
/**
* The data type of the primary key.
*/
protected $keyType = 'string';
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'id_gejala',
'nama_gejala',
];
// Relationships
public function ruleBasis()
{
return $this->hasMany(RuleBasis::class, 'id_gejala', 'id_gejala');
}
/**
* The attributes that should be cast.
*/
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}

View File

@ -2,62 +2,84 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class RuleBasis extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'rule_basis';
/**
* The primary key associated with the table.
*/
protected $primaryKey = 'id_rule';
/**
* FIX: Paksa Laravel pakai 'id_rule' sebagai route key.
* Tanpa ini, Laravel auto-generate nama parameter dari nama class:
* RuleBasis -> 'rule_basi' (typo otomatis) -> UrlGenerationException.
*/
public function getRouteKeyName(): string
{
return 'id_rule';
}
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'id_penyakit',
'id_gejala',
'mb', // ← TAMBAH
'md', // ← TAMBAH
'mb',
'md',
'cf_pakar',
'keterangan',
];
// Relationships
/**
* The attributes that should be cast.
*/
protected $casts = [
'mb' => 'decimal:2',
'md' => 'decimal:2',
'cf_pakar' => 'decimal:2',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* Relationship with MasterPenyakit
*/
public function penyakit()
{
return $this->belongsTo(MasterPenyakit::class, 'id_penyakit', 'id_penyakit');
}
/**
* Relationship with MasterGejala
*/
public function gejala()
{
return $this->belongsTo(MasterGejala::class, 'id_gejala', 'id_gejala');
}
// ============================================
// AUTO-CALCULATE CF dari MB & MD
// ============================================
/**
* Otomatis hitung CF sebelum save
* Auto-calculate CF when MB and MD are set
*/
protected static function boot()
{
parent::boot();
// Event: Sebelum create
static::creating(function ($rule) {
$rule->cf_pakar = $rule->mb - $rule->md;
});
// Event: Sebelum update
static::updating(function ($rule) {
if ($rule->isDirty(['mb', 'md'])) {
$rule->cf_pakar = $rule->mb - $rule->md;
static::saving(function ($model) {
// Auto-calculate CF = MB - MD
if (!is_null($model->mb) && !is_null($model->md)) {
$model->cf_pakar = $model->mb - $model->md;
}
});
}
/**
* Accessor: Hitung CF on-the-fly (alternatif)
*/
public function getCfAttribute()
{
return $this->mb - $this->md;
}
}

View File

@ -0,0 +1,100 @@
@extends('layouts.admin-app')
@section('page-title', 'Tambah Gejala')
@section('page-subtitle', 'Tambah data gejala penyakit baru')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.gejala.store') }}" method="POST">
@csrf
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Tambah Gejala Baru</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- Kode Gejala -->
<div>
<label for="id_gejala" class="block text-sm font-semibold text-gray-700 mb-2">
Kode Gejala <span class="text-red-500">*</span>
</label>
<input
type="text"
name="id_gejala"
id="id_gejala"
value="{{ old('id_gejala') }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition @error('id_gejala') border-red-500 @enderror"
placeholder="Contoh: G001"
required
maxlength="10"
>
<p class="mt-1 text-sm text-gray-500">Format: G001, G002, dst. Maksimal 10 karakter.</p>
@error('id_gejala')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Nama Gejala -->
<div>
<label for="nama_gejala" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Gejala <span class="text-red-500">*</span>
</label>
<input
type="text"
name="nama_gejala"
id="nama_gejala"
value="{{ old('nama_gejala') }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition @error('nama_gejala') border-red-500 @enderror"
placeholder="Contoh: Daun menguning"
required
>
@error('nama_gejala')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.gejala.index') }}" class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white font-bold rounded-xl hover:from-green-600 hover:to-green-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"></path>
</svg>
Simpan Data
</button>
</div>
</div>
</form>
@endsection

View File

@ -0,0 +1,97 @@
@extends('layouts.admin-app')
@section('page-title', 'Edit Gejala')
@section('page-subtitle', 'Edit data gejala penyakit')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.gejala.update', $gejala) }}" method="POST">
@csrf
@method('PUT')
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Edit Gejala</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- Kode Gejala (Read-only) -->
<div>
<label for="id_gejala" class="block text-sm font-semibold text-gray-700 mb-2">
Kode Gejala
</label>
<input
type="text"
name="id_gejala"
id="id_gejala"
value="{{ $gejala->id_gejala }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl bg-gray-100 cursor-not-allowed"
readonly
disabled
>
<p class="mt-1 text-sm text-gray-500">Kode gejala tidak dapat diubah setelah dibuat.</p>
</div>
<!-- Nama Gejala -->
<div>
<label for="nama_gejala" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Gejala <span class="text-red-500">*</span>
</label>
<input
type="text"
name="nama_gejala"
id="nama_gejala"
value="{{ old('nama_gejala', $gejala->nama_gejala) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('nama_gejala') border-red-500 @enderror"
placeholder="Contoh: Daun menguning"
required
>
@error('nama_gejala')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.gejala.index') }}" class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white font-bold rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"></path>
</svg>
Update Data
</button>
</div>
</div>
</form>
@endsection

View File

@ -1,6 +1,6 @@
@extends('layouts.admin-app')
@section('page-title', 'Manajemen Gejala')
@section('page-title', 'Manajemen Gejala')
@section('page-subtitle', 'Kelola data gejala penyakit')
@section('content')
@ -48,7 +48,7 @@
<td class="px-6 py-4 text-sm text-gray-900">{{ $gejalas->firstItem() + $index }}</td>
<td class="px-6 py-4">
<span class="px-3 py-1 text-xs font-bold rounded-full bg-green-100 text-green-800">
{{ $gejala->kode_gejala }}
{{ $gejala->id_gejala }}
</span>
</td>
<td class="px-6 py-4">

View File

@ -1,6 +1,6 @@
@extends('layouts.admin-app')
@section('page-title', ' Tambah Penyakit')
@section('page-title', 'Tambah Penyakit')
@section('page-subtitle', 'Tambah data penyakit tanaman kopi baru')
@section('content')
@ -108,8 +108,8 @@ class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:rin
required
>
<option value="">-- Pilih Kategori --</option>
<option value="Hama" {{ old('kategori') == 'Hama' ? 'selected' : '' }}>🐛 Hama</option>
<option value="Penyakit" {{ old('kategori') == 'Penyakit' ? 'selected' : '' }}>🦠 Penyakit</option>
<option value="Hama" {{ old('kategori') == 'Hama' ? 'selected' : '' }}>Hama</option>
<option value="Penyakit" {{ old('kategori') == 'Penyakit' ? 'selected' : '' }}>Penyakit</option>
</select>
@error('kategori')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>

View File

@ -1,6 +1,6 @@
@extends('layouts.admin-app')
@section('page-title', '✏️ Edit Penyakit')
@section('page-title', 'Edit Penyakit')
@section('page-subtitle', 'Edit data penyakit tanaman kopi')
@section('content')

View File

@ -1,6 +1,6 @@
@extends('layouts.admin-app')
@section('page-title', '🦠 Manajemen Penyakit')
@section('page-title', 'Manajemen Penyakit')
@section('page-subtitle', 'Kelola data penyakit tanaman kopi')
@section('content')
@ -111,11 +111,11 @@ class="w-16 h-16 object-cover rounded-lg shadow-sm cursor-pointer hover:scale-11
<td class="px-4 py-4 whitespace-nowrap">
@if($penyakit->kategori === 'Penyakit')
<span class="px-3 py-1 text-xs font-bold rounded-full bg-red-100 text-red-800">
🦠 Penyakit
Penyakit
</span>
@elseif($penyakit->kategori === 'Hama')
<span class="px-3 py-1 text-xs font-bold rounded-full bg-orange-100 text-orange-800">
🐛 Hama
Hama
</span>
@else
<span class="px-3 py-1 text-xs font-bold rounded-full bg-gray-100 text-gray-800">

View File

@ -0,0 +1,208 @@
@extends('layouts.admin-app')
@section('page-title', ' Tambah Rule Basis')
@section('page-subtitle', 'Tambah aturan diagnosa baru')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.rule-basis.store') }}" method="POST">
@csrf
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-purple-500 to-purple-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Tambah Rule Baru</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- Pilih Penyakit -->
<div>
<label for="id_penyakit" class="block text-sm font-semibold text-gray-700 mb-2">
Penyakit <span class="text-red-500">*</span>
</label>
<select
name="id_penyakit"
id="id_penyakit"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition @error('id_penyakit') border-red-500 @enderror"
required
>
<option value="">-- Pilih Penyakit --</option>
@foreach($penyakits as $penyakit)
<option value="{{ $penyakit->id_penyakit }}" {{ old('id_penyakit') == $penyakit->id_penyakit ? 'selected' : '' }}>
{{ $penyakit->id_penyakit }} - {{ $penyakit->nama_penyakit }}
</option>
@endforeach
</select>
@error('id_penyakit')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Pilih Gejala -->
<div>
<label for="id_gejala" class="block text-sm font-semibold text-gray-700 mb-2">
Gejala <span class="text-red-500">*</span>
</label>
<select
name="id_gejala"
id="id_gejala"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition @error('id_gejala') border-red-500 @enderror"
required
>
<option value="">-- Pilih Gejala --</option>
@foreach($gejalas as $gejala)
<option value="{{ $gejala->id_gejala }}" {{ old('id_gejala') == $gejala->id_gejala ? 'selected' : '' }}>
{{ $gejala->id_gejala }} - {{ $gejala->nama_gejala }}
</option>
@endforeach
</select>
@error('id_gejala')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- MB (Measure of Belief) -->
<div>
<label for="mb" class="block text-sm font-semibold text-gray-700 mb-2">
MB (Measure of Belief) <span class="text-red-500">*</span>
</label>
<input
type="number"
name="mb"
id="mb"
value="{{ old('mb') }}"
step="0.01"
min="0"
max="1"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition @error('mb') border-red-500 @enderror"
placeholder="0.00 - 1.00"
required
oninput="calculateCF()"
>
<p class="mt-1 text-sm text-gray-500">Tingkat kepercayaan gejala menunjukkan penyakit (0.00 - 1.00)</p>
@error('mb')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- MD (Measure of Disbelief) -->
<div>
<label for="md" class="block text-sm font-semibold text-gray-700 mb-2">
MD (Measure of Disbelief) <span class="text-red-500">*</span>
</label>
<input
type="number"
name="md"
id="md"
value="{{ old('md') }}"
step="0.01"
min="0"
max="1"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition @error('md') border-red-500 @enderror"
placeholder="0.00 - 1.00"
required
oninput="calculateCF()"
>
<p class="mt-1 text-sm text-gray-500">Tingkat ketidakyakinan gejala menunjukkan penyakit (0.00 - 1.00)</p>
@error('md')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- CF Preview (Auto-calculated) -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">
CF (Certainty Factor) - Otomatis Dihitung
</label>
<div class="px-4 py-3 bg-purple-50 border border-purple-200 rounded-xl">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-700">CF = MB - MD</span>
<span id="cf_preview" class="text-2xl font-bold text-purple-600">0.00</span>
</div>
</div>
<p class="mt-1 text-sm text-gray-500">Nilai CF akan otomatis dihitung dari MB - MD</p>
</div>
<!-- Keterangan -->
<div>
<label for="keterangan" class="block text-sm font-semibold text-gray-700 mb-2">
Keterangan (Opsional)
</label>
<textarea
name="keterangan"
id="keterangan"
rows="3"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition"
placeholder="Catatan tambahan tentang rule ini..."
>{{ old('keterangan') }}</textarea>
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.rule-basis.index') }}"
class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
{{-- FIX: Tombol Simpan pakai inline style agar warna tidak transparan --}}
<button type="submit"
style="background: linear-gradient(to right, #8b5cf6, #7c3aed); color: white;"
class="px-6 py-3 font-bold rounded-xl shadow-lg hover:shadow-xl transition-all transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"></path>
</svg>
Simpan Rule
</button>
</div>
</div>
</form>
<!-- JavaScript for Auto-calculate CF -->
<script>
function calculateCF() {
const mb = parseFloat(document.getElementById('mb').value) || 0;
const md = parseFloat(document.getElementById('md').value) || 0;
const cf = mb - md;
document.getElementById('cf_preview').textContent = cf.toFixed(2);
const cfPreview = document.getElementById('cf_preview');
if (cf >= 0.7) {
cfPreview.style.color = '#16a34a';
} else if (cf >= 0.4) {
cfPreview.style.color = '#ca8a04';
} else if (cf >= 0) {
cfPreview.style.color = '#ea580c';
} else {
cfPreview.style.color = '#dc2626';
}
}
</script>
@endsection

View File

@ -0,0 +1,189 @@
@extends('layouts.admin-app')
@section('page-title', '✏️ Edit Rule Basis')
@section('page-subtitle', 'Edit aturan diagnosa')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.rule-basis.update', $ruleBasis) }}" method="POST">
@csrf
@method('PUT')
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Edit Rule</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- Penyakit (Read-only) -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">
Penyakit
</label>
<div class="px-4 py-3 bg-gray-100 border border-gray-300 rounded-xl">
<span class="px-2 py-1 bg-red-100 text-red-800 rounded-full text-xs font-bold mr-2">
{{ $ruleBasis->id_penyakit }}
</span>
<span class="text-gray-900 font-semibold">{{ $ruleBasis->penyakit->nama_penyakit ?? '-' }}</span>
</div>
<p class="mt-1 text-sm text-gray-500">Penyakit tidak dapat diubah setelah rule dibuat</p>
</div>
<!-- Gejala (Read-only) -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">
Gejala
</label>
<div class="px-4 py-3 bg-gray-100 border border-gray-300 rounded-xl">
<span class="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-bold mr-2">
{{ $ruleBasis->id_gejala }}
</span>
<span class="text-gray-900">{{ $ruleBasis->gejala->nama_gejala ?? '-' }}</span>
</div>
<p class="mt-1 text-sm text-gray-500">Gejala tidak dapat diubah setelah rule dibuat</p>
</div>
<!-- MB (Measure of Belief) -->
<div>
<label for="mb" class="block text-sm font-semibold text-gray-700 mb-2">
MB (Measure of Belief) <span class="text-red-500">*</span>
</label>
<input
type="number"
name="mb"
id="mb"
value="{{ old('mb', $ruleBasis->mb) }}"
step="0.01"
min="0"
max="1"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('mb') border-red-500 @enderror"
required
oninput="calculateCF()"
>
<p class="mt-1 text-sm text-gray-500">Tingkat kepercayaan gejala menunjukkan penyakit (0.00 - 1.00)</p>
@error('mb')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- MD (Measure of Disbelief) -->
<div>
<label for="md" class="block text-sm font-semibold text-gray-700 mb-2">
MD (Measure of Disbelief) <span class="text-red-500">*</span>
</label>
<input
type="number"
name="md"
id="md"
value="{{ old('md', $ruleBasis->md) }}"
step="0.01"
min="0"
max="1"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('md') border-red-500 @enderror"
required
oninput="calculateCF()"
>
<p class="mt-1 text-sm text-gray-500">Tingkat ketidakyakinan gejala menunjukkan penyakit (0.00 - 1.00)</p>
@error('md')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- CF Preview (Auto-calculated) -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">
CF (Certainty Factor) - Otomatis Dihitung
</label>
<div class="px-4 py-3 bg-blue-50 border border-blue-200 rounded-xl">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-700">CF = MB - MD</span>
<span id="cf_preview" class="text-2xl font-bold text-blue-600">{{ number_format($ruleBasis->cf_pakar, 2) }}</span>
</div>
</div>
<p class="mt-1 text-sm text-gray-500">Nilai CF akan otomatis dihitung dari MB - MD</p>
</div>
<!-- Keterangan -->
<div>
<label for="keterangan" class="block text-sm font-semibold text-gray-700 mb-2">
Keterangan (Opsional)
</label>
<textarea
name="keterangan"
id="keterangan"
rows="3"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Catatan tambahan tentang rule ini..."
>{{ old('keterangan', $ruleBasis->keterangan) }}</textarea>
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.rule-basis.index') }}" class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white font-bold rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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"></path>
</svg>
Update Rule
</button>
</div>
</div>
</form>
<!-- JavaScript for Auto-calculate CF -->
<script>
function calculateCF() {
const mb = parseFloat(document.getElementById('mb').value) || 0;
const md = parseFloat(document.getElementById('md').value) || 0;
const cf = mb - md;
document.getElementById('cf_preview').textContent = cf.toFixed(2);
// Change color based on CF value
const cfPreview = document.getElementById('cf_preview');
if (cf >= 0.7) {
cfPreview.className = 'text-2xl font-bold text-green-600';
} else if (cf >= 0.4) {
cfPreview.className = 'text-2xl font-bold text-yellow-600';
} else if (cf >= 0) {
cfPreview.className = 'text-2xl font-bold text-orange-600';
} else {
cfPreview.className = 'text-2xl font-bold text-red-600';
}
}
// Calculate on page load
window.addEventListener('DOMContentLoaded', calculateCF);
</script>
@endsection

View File

@ -0,0 +1,239 @@
@extends('layouts.admin-app')
@section('page-title', '🧠 Manajemen Rule Basis')
@section('page-subtitle', 'Kelola aturan diagnosa sistem pakar')
@section('content')
@if (session('success'))
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">{{ session('success') }}</span>
</div>
@endif
<!-- Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Total Rule</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalRules }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-purple-400 to-purple-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Rata-rata CF</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ number_format($avgCF ?? 0, 2) }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-blue-400 to-blue-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">CF Tinggi (≥0.7)</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $highConfidenceRules }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-green-400 to-green-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Penyakit Terintegrasi</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalPenyakitWithRules }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-red-400 to-red-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="bg-gradient-to-r from-purple-500 to-purple-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Daftar Rule Basis</h3>
</div>
{{-- FIX: Tombol Tambah Rule - warna solid purple --}}
<a href="{{ route('admin.rule-basis.create') }}"
style="background: linear-gradient(to right, #8b5cf6, #7c3aed); color: white;"
class="flex items-center px-5 py-3 font-bold rounded-xl shadow-lg hover:shadow-xl transition-all transform hover:scale-105">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Tambah Rule
</a>
</div>
</div>
<!-- Filter -->
<div class="p-6 bg-gray-50 border-b border-gray-200">
<form method="GET" action="{{ route('admin.rule-basis.index') }}" class="flex gap-4">
<div class="flex-1">
<label for="penyakit" class="block text-sm font-semibold text-gray-700 mb-2">Filter Penyakit</label>
<select name="penyakit" id="penyakit" class="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="">Semua Penyakit</option>
@foreach($penyakits as $penyakit)
<option value="{{ $penyakit->id_penyakit }}" {{ request('penyakit') == $penyakit->id_penyakit ? 'selected' : '' }}>
{{ $penyakit->id_penyakit }} - {{ $penyakit->nama_penyakit }}
</option>
@endforeach
</select>
</div>
<div class="flex-1">
<label for="gejala" class="block text-sm font-semibold text-gray-700 mb-2">Filter Gejala</label>
<select name="gejala" id="gejala" class="w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="">Semua Gejala</option>
@foreach($gejalas as $gejala)
<option value="{{ $gejala->id_gejala }}" {{ request('gejala') == $gejala->id_gejala ? 'selected' : '' }}>
{{ $gejala->id_gejala }} - {{ $gejala->nama_gejala }}
</option>
@endforeach
</select>
</div>
<div class="flex items-end gap-2">
<button type="submit" style="background-color: #8b5cf6; color: white;" class="px-6 py-2 font-semibold rounded-xl hover:opacity-90 transition">
Filter
</button>
@if(request('penyakit') || request('gejala'))
<a href="{{ route('admin.rule-basis.index') }}" class="px-6 py-2 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Reset
</a>
@endif
</div>
</form>
</div>
<!-- Table -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">No</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Penyakit</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Gejala</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">MB</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">MD</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">CF</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Keterangan</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($rules as $index => $rule)
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 text-sm text-gray-900">{{ $rules->firstItem() + $index }}</td>
<td class="px-6 py-4">
<div class="text-sm">
<span class="px-2 py-1 bg-red-100 text-red-800 rounded-full text-xs font-bold">
{{ $rule->id_penyakit }}
</span>
<p class="mt-1 font-semibold text-gray-900">{{ $rule->penyakit->nama_penyakit ?? '-' }}</p>
</div>
</td>
<td class="px-6 py-4">
<div class="text-sm">
<span class="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs font-bold">
{{ $rule->id_gejala }}
</span>
<p class="mt-1 text-gray-700">{{ $rule->gejala->nama_gejala ?? '-' }}</p>
</div>
</td>
<td class="px-6 py-4 text-center">
<span class="px-3 py-1 bg-blue-100 text-blue-800 rounded-lg text-sm font-bold">
{{ number_format($rule->mb, 2) }}
</span>
</td>
<td class="px-6 py-4 text-center">
<span class="px-3 py-1 bg-orange-100 text-orange-800 rounded-lg text-sm font-bold">
{{ number_format($rule->md, 2) }}
</span>
</td>
<td class="px-6 py-4 text-center">
<span class="px-3 py-1 bg-purple-100 text-purple-800 rounded-lg text-sm font-bold">
{{ number_format($rule->cf_pakar, 2) }}
</span>
</td>
<td class="px-6 py-4 text-sm text-gray-600 max-w-xs truncate">
{{ $rule->keterangan ?? '-' }}
</td>
<td class="px-6 py-4 text-center">
<div class="flex justify-center space-x-2">
{{-- FIX: Gunakan id_rule eksplisit untuk route model binding --}}
<a href="{{ route('admin.rule-basis.edit', $rule->id_rule) }}"
class="inline-flex items-center px-3 py-2 bg-blue-500 text-white text-xs font-semibold rounded-lg hover:bg-blue-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
Edit
</a>
{{-- FIX: Form delete dengan id_rule eksplisit --}}
<form action="{{ route('admin.rule-basis.destroy', $rule->id_rule) }}"
method="POST"
onsubmit="return confirm('Yakin ingin menghapus rule ini?')"
style="display: inline;">
@csrf
@method('DELETE')
<button type="submit" class="inline-flex items-center px-3 py-2 bg-red-500 text-white text-xs font-semibold rounded-lg hover:bg-red-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="px-6 py-12 text-center">
<div class="text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p class="text-xl font-semibold mb-2">Belum ada rule basis</p>
<p class="text-sm">Klik "Tambah Rule" untuk menambahkan aturan diagnosa</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $rules->links() }}
</div>
</div>
@endsection

View File

@ -185,6 +185,19 @@ class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ req
</div>
</a>
<!-- Rule Basis Management -->
<a href="{{ route('admin.rule-basis.index') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.rule-basis.*') ? 'active bg-gray-700' : '' }} group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-purple-500/20 group-hover:bg-purple-500/30 transition-colors">
<svg class="w-5 h-5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold">Rule Basis</span>
</div>
</a>
<!-- Artikel Management -->
<a href="{{ route('admin.artikel.index') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.artikel.*') ? 'active bg-gray-700' : '' }} group">

View File

@ -5,7 +5,8 @@
use App\Http\Controllers\SuperAdmin\UserManagementController;
use App\Http\Controllers\Admin\AdminDashboardController; // ← TAMBAHKAN INI
use App\Http\Controllers\Admin\PenyakitController; // ← DAN INI
use App\Http\Controllers\Admin\GejalaController; // ← DAN INI
use App\Http\Controllers\Admin\GejalaController;
use App\Http\Controllers\Admin\RuleBasisController; // ← DAN INI
use App\Http\Controllers\Admin\ArtikelController; // ← DAN INI
Route::get('/', function () {
@ -47,6 +48,11 @@
// Gejala Management
Route::resource('gejala', GejalaController::class);
//Rule Basis Management
Route::resource('rule-basis', RuleBasisController::class)->parameters([
'rule-basis' => 'ruleBasis'
]);
// Artikel Management
Route::resource('artikel', ArtikelController::class);