gastrosaw/resources/views/proses_saw/index.blade.php

511 lines
18 KiB
PHP

@extends('layouts.app')
@section('title', 'Proses SAW')
@section('content')
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.css" rel="stylesheet">
<div class="max-w-7xl mx-auto space-y-8">
<!-- HEADER -->
<div class="flex flex-col md:flex-row md:items-end md:justify-between gap-5">
<div>
<h1 class="text-2xl sm:text-3xl lg:text-4xl font-bold tracking-tight text-gray-800">
Proses Rekomendasi
</h1>
<p class="text-gray-500 mt-2 text-base max-w-2xl">
Pilih data pasien untuk menjalankan proses perhitungan rekomendasi makanan
menggunakan metode Simple Additive Weighting (SAW).
</p>
</div>
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-green-50 text-green-700 text-sm font-semibold border border-green-100 w-fit">
<i class="ri-calculator-line text-lg"></i>
Metode SAW
</div>
</div>
<!-- ALERT -->
@if(session('error'))
<div class="bg-red-50 border border-red-100 text-red-600 px-5 py-4 rounded-2xl text-sm font-medium">
{{ session('error') }}
</div>
@endif
@if(session('success'))
<div class="bg-green-50 border border-green-100 text-green-700 px-5 py-4 rounded-2xl text-sm font-medium">
{{ session('success') }}
</div>
@endif
<!-- MAIN CARD -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- LEFT PANEL -->
<div class="lg:col-span-1">
<div class="bg-white border border-gray-200 rounded-3xl shadow-sm overflow-hidden">
<div class="p-7 border-b border-gray-100 bg-gradient-to-br from-green-50 to-white">
<div class="w-14 h-14 rounded-2xl bg-green-100 text-green-700 flex items-center justify-center text-2xl mb-5">
<i class="ri-user-search-line"></i>
</div>
<h2 class="text-2xl font-bold text-gray-800">
Pilih Pasien
</h2>
<p class="text-sm text-gray-500 mt-2 leading-6">
Ketik nama pasien pada kolom pencarian agar lebih mudah menemukan data pasien.
</p>
</div>
<div class="p-7">
<form id="form-proses" action="{{ route('proses_saw.proses') }}" method="POST">
@csrf
<!-- SELECT PASIEN -->
<div>
<label class="block text-xs font-bold uppercase tracking-wide text-gray-500 mb-3">
Cari / Pilih Pasien
</label>
<select id="pasien_id" name="pasien_id" required>
<option value=""></option>
@foreach($pasiens as $pasien)
<option value="{{ $pasien->id }}"
data-nama="{{ $pasien->nama }}"
data-umur="{{ $pasien->umur }}"
data-jenis_kelamin="{{ $pasien->jenis_kelamin }}"
data-gejala="{{ $pasien->gejala }}"
data-aktivitas="{{ $pasien->aktivitas_harian }}"
data-berat="{{ $pasien->berat_badan }}"
data-tinggi="{{ $pasien->tinggi_badan }}">
{{ $pasien->nama }} - {{ $pasien->umur }} tahun
</option>
@endforeach
</select>
<p class="text-xs text-gray-400 mt-3 leading-5">
Anda dapat mencari berdasarkan nama pasien, umur, atau gejala.
</p>
</div>
<!-- BUTTON -->
<button type="submit" id="btn-proses"
class="mt-7 w-full inline-flex items-center justify-center gap-3 px-6 py-4 text-sm font-bold rounded-2xl bg-green-600 text-white hover:bg-green-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition shadow-sm"
disabled>
<svg id="spinner" class="hidden w-5 h-5 animate-spin"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8v8z"></path>
</svg>
<i id="btn-icon" class="ri-calculator-line text-lg"></i>
<span id="btn-text">
Proses Rekomendasi
</span>
</button>
</form>
</div>
</div>
</div>
<!-- RIGHT PANEL -->
<div class="lg:col-span-2">
<div class="bg-white border border-gray-200 rounded-3xl shadow-sm overflow-hidden">
<!-- CARD HEADER -->
<div class="px-8 py-6 border-b border-gray-100 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-gray-800">
Informasi Pasien
</h2>
<p class="text-sm text-gray-500 mt-1">
Data pasien akan muncul setelah pasien dipilih.
</p>
</div>
<div id="status-badge"
class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gray-100 text-gray-500 text-xs font-bold uppercase tracking-wide w-fit">
<span class="w-2 h-2 rounded-full bg-gray-400"></span>
Belum Dipilih
</div>
</div>
<!-- EMPTY STATE -->
<div id="empty-state" class="px-8 py-16 text-center">
<div class="w-20 h-20 mx-auto rounded-full bg-green-50 text-green-600 flex items-center justify-center text-4xl mb-5">
<i class="ri-user-heart-line"></i>
</div>
<h3 class="text-xl font-bold text-gray-800">
Belum ada pasien dipilih
</h3>
<p class="text-sm text-gray-500 mt-2 max-w-md mx-auto leading-6">
Silakan cari dan pilih pasien pada panel sebelah kiri untuk melihat detail data pasien sebelum proses SAW dilakukan.
</p>
</div>
<!-- INFO CONTENT -->
<div id="info-content" class="hidden p-8 space-y-7">
<!-- PATIENT NAME CARD -->
<div class="bg-gradient-to-r from-green-50 to-white border border-green-100 rounded-3xl p-7">
<p class="text-xs font-bold uppercase tracking-wide text-green-600 mb-2">
Pasien Terpilih
</p>
<h3 id="nama" class="text-3xl font-black text-gray-900">
-
</h3>
<p class="text-sm text-gray-500 mt-2">
Data pasien siap digunakan untuk proses rekomendasi.
</p>
</div>
<!-- INFO GRID -->
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
<div class="info-card">
<div class="info-icon bg-blue-50 text-blue-600">
<i class="ri-calendar-line"></i>
</div>
<div>
<p class="info-label">Umur</p>
<p id="umur" class="info-value">-</p>
</div>
</div>
<div class="info-card">
<div class="info-icon bg-purple-50 text-purple-600">
<i class="ri-user-line"></i>
</div>
<div>
<p class="info-label">Jenis Kelamin</p>
<p id="jenis_kelamin" class="info-value">-</p>
</div>
</div>
<div class="info-card">
<div class="info-icon bg-orange-50 text-orange-600">
<i class="ri-run-line"></i>
</div>
<div>
<p class="info-label">Aktivitas</p>
<p id="aktivitas_harian" class="info-value">-</p>
</div>
</div>
<div class="info-card">
<div class="info-icon bg-teal-50 text-teal-600">
<i class="ri-scales-3-line"></i>
</div>
<div>
<p class="info-label">Berat Badan</p>
<p id="berat" class="info-value">-</p>
</div>
</div>
<div class="info-card">
<div class="info-icon bg-cyan-50 text-cyan-600">
<i class="ri-ruler-line"></i>
</div>
<div>
<p class="info-label">Tinggi Badan</p>
<p id="tinggi" class="info-value">-</p>
</div>
</div>
<div class="info-card">
<div class="info-icon bg-red-50 text-red-600">
<i class="ri-heart-pulse-line"></i>
</div>
<div>
<p class="info-label">Gejala</p>
<p id="gejala" class="info-value text-red-600">-</p>
</div>
</div>
</div>
<!-- NOTE -->
<div class="bg-gray-50 border border-gray-100 rounded-2xl p-5">
<div class="flex items-start gap-3">
<div class="w-9 h-9 rounded-xl bg-white border border-gray-200 flex items-center justify-center text-green-600 shrink-0">
<i class="ri-information-line"></i>
</div>
<div>
<h4 class="text-sm font-bold text-gray-800">
Informasi Proses
</h4>
<p class="text-sm text-gray-500 mt-1 leading-6">
Sistem akan memfilter makanan berdasarkan gejala pasien, menghitung normalisasi kriteria,
mengalikan dengan bobot, lalu menghasilkan ranking rekomendasi makanan terbaik.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.ts-wrapper.single .ts-control {
border: 1px solid #e5e7eb;
border-radius: 1rem;
padding: 0.95rem 1rem;
background: #f9fafb;
font-size: 0.875rem;
color: #374151;
box-shadow: none;
}
.ts-wrapper.focus .ts-control {
border-color: #22c55e;
background: #ffffff;
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.12);
}
.ts-dropdown {
border-radius: 1rem;
border: 1px solid #e5e7eb;
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.10);
overflow: hidden;
margin-top: 0.5rem;
}
.ts-dropdown .option {
padding: 0.85rem 1rem;
font-size: 0.875rem;
}
.ts-dropdown .active {
background: #f0fdf4;
color: #15803d;
}
.info-card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 1.5rem;
padding: 1.25rem;
display: flex;
align-items: center;
gap: 1rem;
transition: 0.2s ease;
}
.info-card:hover {
background: #f9fafb;
}
.info-icon {
width: 46px;
height: 46px;
border-radius: 1rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.35rem;
flex-shrink: 0;
}
.info-label {
font-size: 0.72rem;
font-weight: 700;
color: #9ca3af;
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 0.25rem;
}
.info-value {
font-size: 0.95rem;
font-weight: 700;
color: #1f2937;
line-height: 1.4;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js"></script>
<script>
const select = document.getElementById('pasien_id');
const btn = document.getElementById('btn-proses');
const btnText = document.getElementById('btn-text');
const btnIcon = document.getElementById('btn-icon');
const spinner = document.getElementById('spinner');
const emptyState = document.getElementById('empty-state');
const infoContent = document.getElementById('info-content');
const statusBadge = document.getElementById('status-badge');
const fields = {
nama: document.getElementById('nama'),
umur: document.getElementById('umur'),
jenis_kelamin: document.getElementById('jenis_kelamin'),
gejala: document.getElementById('gejala'),
aktivitas: document.getElementById('aktivitas_harian'),
berat: document.getElementById('berat'),
tinggi: document.getElementById('tinggi'),
};
function setStatusSelected() {
statusBadge.className =
'inline-flex items-center gap-2 px-4 py-2 rounded-full bg-green-50 text-green-700 text-xs font-bold uppercase tracking-wide w-fit';
statusBadge.innerHTML =
'<span class="w-2 h-2 rounded-full bg-green-500"></span> Siap Diproses';
}
function setStatusEmpty() {
statusBadge.className =
'inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gray-100 text-gray-500 text-xs font-bold uppercase tracking-wide w-fit';
statusBadge.innerHTML =
'<span class="w-2 h-2 rounded-full bg-gray-400"></span> Belum Dipilih';
}
function resetFields() {
fields.nama.innerText = '-';
fields.umur.innerText = '-';
fields.jenis_kelamin.innerText = '-';
fields.gejala.innerText = '-';
fields.aktivitas.innerText = '-';
fields.berat.innerText = '-';
fields.tinggi.innerText = '-';
btn.disabled = true;
emptyState.classList.remove('hidden');
infoContent.classList.add('hidden');
setStatusEmpty();
}
function fillPatientData(value) {
if (!value) {
resetFields();
return;
}
let option = select.querySelector('option[value="' + value + '"]');
if (!option) {
resetFields();
return;
}
fields.nama.innerText = option.dataset.nama || '-';
fields.umur.innerText = (option.dataset.umur || '-') + ' tahun';
fields.jenis_kelamin.innerText = option.dataset.jenis_kelamin || '-';
fields.gejala.innerText = option.dataset.gejala || '-';
fields.aktivitas.innerText = option.dataset.aktivitas || '-';
fields.berat.innerText = (option.dataset.berat || '-') + ' kg';
fields.tinggi.innerText = (option.dataset.tinggi || '-') + ' cm';
btn.disabled = false;
emptyState.classList.add('hidden');
infoContent.classList.remove('hidden');
setStatusSelected();
}
const selectPasien = new TomSelect('#pasien_id', {
create: false,
allowEmptyOption: true,
placeholder: 'Cari nama pasien...',
sortField: {
field: 'text',
direction: 'asc'
},
searchField: ['text'],
onFocus: function () {
this.control_input.placeholder = '';
},
onBlur: function () {
if (!this.getValue()) {
this.control_input.placeholder = 'Cari nama pasien...';
}
},
onChange: function(value) {
fillPatientData(value);
},
render: {
no_results: function(data, escape) {
return `
<div class="px-4 py-3 text-sm text-gray-500">
Pasien tidak ditemukan
</div>
`;
}
}
});
document.getElementById('form-proses').addEventListener('submit', function () {
btn.disabled = true;
spinner.classList.remove('hidden');
btnIcon.classList.add('hidden');
btnText.innerText = 'Memproses...';
});
</script>
@endsection