admin-gejala dan rule basis done
This commit is contained in:
parent
9d860df6e8
commit
86c6a51401
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
];
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue