perubahan besar-besaran

This commit is contained in:
Alfiansyahp2 2025-05-22 08:46:05 +07:00
parent 6c2629aad5
commit e8c1e4bbad
38 changed files with 1503 additions and 401 deletions

View File

@ -43,11 +43,15 @@ public function admindash()
// USER
public function datauser(){
$users = User::with('role')->get(); // Eager load the role relationship
$users = User::whereHas('role', function ($query) {
$query->where('name', 'user');
})->with('role')->get();
return view('admin.datauser.datauser', compact('users'));
}
public function edituser(User $user){
// Ambil semua role dari tabel roles
$roles = Role::pluck('name', 'id'); // Mengambil nama dan id role

View File

@ -12,6 +12,15 @@
use App\Models\JenisMakanan;
use App\Http\Requests\JenisMakananRequest;
use App\Http\Requests\UpdateJenisMakananRequest;
use App\Models\Komponen;
// use App\Http\Requests\JenisMakananRequest;
// use App\Http\Requests\UpdateJenisMakananRequest;
use App\Models\WaktuMakan;
// use App\Http\Requests\JenisMakananRequest;
// use App\Http\Requests\UpdateJenisMakananRequest;
use App\Models\MakananKomponenWaktu;
// use App\Http\Requests\JenisMakananRequest;
// use App\Http\Requests\UpdateJenisMakananRequest;
class MakananController extends Controller
{
@ -96,8 +105,6 @@ public function deletemakanan(Makanan $makanan) {
public function kategori(){
$kategoris = Kategori::all();
return view('admin.kategori.kategori', compact('kategoris'));
@ -127,11 +134,6 @@ public function deletekategori(Kategori $kategori) {
public function jenismakanan()
{
$jenis_makanans = JenisMakanan::all();
@ -167,4 +169,162 @@ public function deletejenismakanan(JenisMakanan $jenis)
$jenis->delete();
return redirect()->route('jenismakanan')->with('success', 'Jenis makanan berhasil dihapus');
}
public function komponen(){
$komponens = Komponen::all();
return view('admin.komponen.komponen', compact('komponens'));
}
public function editkomponen(Komponen $komponen){
return view('admin.komponen.editkomponen', compact('komponen'));
}
public function tambahkomponen(){
return view('admin.komponen.tambahkomponen');
}
// public function storekomponen(KategoriRequest $request){
// $validatedData = $request->validated();
// Kategori::create($validatedData);
// return redirect()->route('kategori');
// }
// public function updatekategori(UpdateKategoriRequest $request, Kategori $kategori)
// {
// $validatedData = $request->validated();
// $kategori->update($validatedData);
// return redirect()->route('kategori');
// }
public function deletekomponen(Komponen $komponen) {
$komponen->delete();
return redirect()->route('komponen')->with('success', 'user deleted successfully');
}
public function waktumakan(){
$waktu_makans = WaktuMakan::all();
return view('admin.waktumakan.waktumakan', compact('waktu_makans'));
}
public function editwaktumakan(WaktuMakan $waktumakan){
return view('admin.waktumakan.editwaktumakan', compact('waktumakan'));
}
public function tambahwaktumakan(){
return view('admin.waktumakan.tambahwaktumakan');
}
// public function storekomponen(KategoriRequest $request){
// $validatedData = $request->validated();
// Kategori::create($validatedData);
// return redirect()->route('kategori');
// }
// public function updatekategori(UpdateKategoriRequest $request, Kategori $kategori)
// {
// $validatedData = $request->validated();
// $kategori->update($validatedData);
// return redirect()->route('kategori');
// }
public function deletewaktumakan(WaktuMakan $waktumakan) {
$waktumakan->delete();
return redirect()->route('waktumakan')->with('success', 'user deleted successfully');
}
public function index(Request $request)
{
$query = MakananKomponenWaktu::with(['makanan', 'komponen', 'waktuMakan']);
if ($request->komponen_id) {
$query->where('komponen_id', $request->komponen_id);
}
if ($request->waktu_makan_id) {
$query->where('waktu_makan_id', $request->waktu_makan_id);
}
$relasis = $query->get();
$makanans = Makanan::all();
$komponens = Komponen::all();
$waktuMakans = WaktuMakan::all();
return view('admin.relasi.relasi', compact('relasis', 'makanans', 'komponens', 'waktuMakans'));
}
public function store(Request $request)
{
$request->validate([
'makanan_id' => 'required|exists:makanans,id',
'komponen_id' => 'required|exists:komponens,id',
'waktu_makan_id' => 'required|exists:waktu_makans,id',
]);
// Cegah duplikasi
$exists = MakananKomponenWaktu::where([
'makanan_id' => $request->makanan_id,
'komponen_id' => $request->komponen_id,
'waktu_makan_id' => $request->waktu_makan_id
])->exists();
if (!$exists) {
MakananKomponenWaktu::create($request->only(['makanan_id', 'komponen_id', 'waktu_makan_id']));
return redirect()->back()->with('success', 'Relasi berhasil ditambahkan.');
} else {
return redirect()->back()->with('error', 'Relasi sudah ada.');
}
}
public function edit($id)
{
$relasi = MakananKomponenWaktu::findOrFail($id);
$makanans = Makanan::all();
$komponens = Komponen::all();
$waktuMakans = WaktuMakan::all();
return view('admin.relasi.edit', compact('relasi', 'makanans', 'komponens', 'waktuMakans'));
}
public function update(Request $request, $id)
{
$request->validate([
'makanan_id' => 'required|exists:makanans,id',
'komponen_id' => 'required|exists:komponens,id',
'waktu_makan_id' => 'required|exists:waktu_makans,id',
]);
$relasi = MakananKomponenWaktu::findOrFail($id);
// Cek apakah ada duplikasi selain record yang sedang diupdate
$exists = MakananKomponenWaktu::where('id', '!=', $id)
->where('makanan_id', $request->makanan_id)
->where('komponen_id', $request->komponen_id)
->where('waktu_makan_id', $request->waktu_makan_id)
->exists();
if ($exists) {
return redirect()->back()->with('error', 'Relasi sudah ada.');
}
$relasi->update([
'makanan_id' => $request->makanan_id,
'komponen_id' => $request->komponen_id,
'waktu_makan_id' => $request->waktu_makan_id,
]);
return redirect()->route('relasi')->with('success', 'Relasi berhasil diperbarui.');
}
public function destroy($id)
{
$relasi = MakananKomponenWaktu::findOrFail($id);
$relasi->delete();
return redirect()->back()->with('success', 'Relasi berhasil dihapus.');
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\WaktuMakan;
use App\Models\Komponen;
use App\Models\Makanan;
use App\Models\PengecualianMakanan;
use Illuminate\Support\Facades\Auth;
class PengecualianController extends Controller
{
//
public function form()
{
$waktuMakans = WaktuMakan::all();
$komponens = Komponen::all();
$makanans = Makanan::all(); // Atau filter berdasarkan waktu & komponen tertentu
return view('pengecualian.form', compact('waktuMakans', 'komponens', 'makanans'));
}
public function simpan(Request $request)
{
foreach ($request->pengecualian as $makananId) {
PengecualianMakanan::updateOrCreate([
'user_id' => Auth::id(),
'makanan_id' => $makananId,
'waktu_makan_id' => $request->waktu_makan_id,
'komponen_id' => $request->komponen_id,
]);
}
return redirect()->route('pengecualian.form')->with('success', 'Pengecualian berhasil disimpan!');
}
}

View File

@ -27,7 +27,6 @@ public function prosesSementara(Request $request)
$kriterias = Kriteria::all();
$nilai = [];
// Hapus data lama (opsional, jika ingin reset)
foreach ($kriterias as $baris) {
foreach ($kriterias as $kolom) {
if ($baris->id != $kolom->id) {
@ -39,7 +38,6 @@ public function prosesSementara(Request $request)
}
}
// Membuat matriks perbandingan dan menyimpannya ke session dan DB
foreach ($kriterias as $baris) {
foreach ($kriterias as $kolom) {
if ($baris->id == $kolom->id) {
@ -87,7 +85,6 @@ public function hasilNormalisasi()
return redirect()->route('perbandingan')->with('error', 'Data perbandingan belum tersedia.');
}
// Hitung jumlah per kolom
$jumlahKolom = [];
foreach ($kriterias as $kriteria) {
$id = $kriteria->id;
@ -97,7 +94,6 @@ public function hasilNormalisasi()
}
}
// Normalisasi
$normalisasi = [];
foreach ($kriterias as $baris) {
$row = [];
@ -107,13 +103,11 @@ public function hasilNormalisasi()
$normalisasi[] = $row;
}
// Hitung bobot
$bobot = [];
foreach ($normalisasi as $baris) {
$bobot[] = array_sum($baris) / count($baris);
}
// Simpan bobot ke database dan session
foreach ($bobot as $index => $value) {
BobotKriteria::updateOrCreate(
['kriteria_id' => $kriterias[$index]->id],
@ -129,17 +123,4 @@ public function hasilNormalisasi()
'bobot' => $bobot
]);
}
public function perbandinganAlternatif()
{
$kriterias = Kriteria::all();
$alternatifs = Makanan::all();
$bobot = Session::get('bobot_kriteria');
if (!$bobot) {
return redirect()->route('hasil.normalisasi')->with('error', 'Bobot kriteria belum tersedia.');
}
return view('admin.alternatif.perbandingan', compact('kriterias', 'alternatifs', 'bobot'));
}
}

View File

@ -8,105 +8,131 @@
use App\Models\BobotKriteria;
use App\Models\SkorMakanan;
use App\Models\Rekomendasi;
use Illuminate\Support\Facades\Auth; // pastikan ini ada di atas
use App\Models\WaktuMakan;
use App\Models\Komponen;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class RekomendasiController extends Controller
{
//
public function hitungDanSimpan()
public function hitungDanSimpanOtomatis()
{
$idAlternatif = session('alternatifs_dipilih');
if (!$idAlternatif || count($idAlternatif) < 2) {
return redirect()->route('alternatif.pilih')->with('error', 'Alternatif belum dipilih atau kurang dari dua.');
}
$alternatifs = Makanan::whereIn('id', $idAlternatif)->get();
$kriterias = Kriteria::all();
$bobotKriterias = BobotKriteria::pluck('bobot', 'kriteria_id');
$defaultBobots = BobotKriteria::pluck('bobot', 'kriteria_id');
$waktuMakans = WaktuMakan::all();
$komponens = Komponen::all();
foreach ($alternatifs as $alternatif) {
$nilaiAkhir = 0;
SkorMakanan::truncate();
Rekomendasi::where('user_id', Auth::id())->delete();
foreach ($kriterias as $kriteria) {
$bobotKriteria = $bobotKriterias[$kriteria->id] ?? 0;
$bobotAlternatif = SkorMakanan::where('kriteria_id', $kriteria->id)
->where('makanan_id', $alternatif->id)
->value('nilai') ?? 0;
foreach ($waktuMakans as $waktu) {
// Ambil preferensi bobot kriteria untuk waktu makan ini
$preferensiBobot = \App\Models\PreferensiWaktuKriteria::where('waktu_makan_id', $waktu->id)
->pluck('bobot', 'kriteria_id');
$nilaiAkhir += $bobotKriteria * $bobotAlternatif;
foreach ($komponens as $komponen) {
$makanans = Makanan::whereHas('komponenWaktu', function ($query) use ($komponen, $waktu) {
$query->where('komponen_id', $komponen->id)
->where('waktu_makan_id', $waktu->id);
})->get();
if ($makanans->isEmpty()) continue;
$total = [
'lemak' => $makanans->sum('lemak') ?: 1,
'natrium' => $makanans->sum('natrium') ?: 1,
'energi' => $makanans->sum('energi') ?: 1,
'karbohidrat' => $makanans->sum('karbohidrat') ?: 1,
];
foreach ($makanans as $makanan) {
$skor = 0;
foreach ($kriterias as $kriteria) {
$attr = strtolower($kriteria->nama);
$nilai = $makanan->$attr ?? 0;
// Normalisasi
$normalized = in_array($attr, ['lemak', 'natrium'])
? 1 - ($nilai / $total[$attr])
: $nilai / $total[$attr];
// Gunakan preferensi waktu jika ada, jika tidak fallback
$bobot = $preferensiBobot[$kriteria->id] ?? $defaultBobots[$kriteria->id] ?? 0;
$skor += $normalized * $bobot;
SkorMakanan::updateOrCreate([
'kriteria_id' => $kriteria->id,
'makanan_id' => $makanan->id,
], [
'nilai' => $normalized
]);
}
// Simpan nilai akhir ke rekomendasi
Rekomendasi::updateOrCreate(
[
'user_id' => Auth::id(),
'makanan_id' => $makanan->id,
'komponen_id' => $komponen->id,
'waktu_makan_id' => $waktu->id,
],
[
'nilai_akhir' => $skor,
'tanggal_rekomendasi' => now(),
]
);
}
}
Rekomendasi::updateOrCreate(
['makanan_id' => $alternatif->id, 'user_id' => Auth::id()],
[
'nilai_akhir' => $nilaiAkhir,
'tanggal_rekomendasi' => now()->toDateString()
]
);
}
return redirect()->route('rekomendasi.hasil')->with('success', 'Rekomendasi berhasil dihitung dan disimpan.');
return redirect()->route('rekomendasi.hasil')->with('success', 'Rekomendasi berhasil dihitung dengan bobot preferensi waktu.');
}
// 2. Tampilkan hasil rekomendasi
public function tampil()
{
// 1. Ambil & kelompokkan data
$rekomendasiByDate = Rekomendasi::with('makanan')
->where('nilai_akhir', '>', 0)
->orderByDesc('tanggal_rekomendasi') // terbaru di depan carousel
->orderByDesc('nilai_akhir') // ranking per tanggal
->get()
->groupBy('tanggal_rekomendasi'); // hasil: Collection keyed by yyyy-mm-dd
public function tampilHasil()
{
$rekomendasi = Rekomendasi::with('makanan', 'komponen', 'waktuMakan')
->where('user_id', Auth::id())
// ->whereDate('tanggal_rekomendasi', now())
->get();
// 2. Daftar tanggal unik untuk dropdown hapus
$tanggalList = $rekomendasiByDate->keys(); // Collection of dates
// Urutan komponen manual
$urutanKomponen = ['karbohidrat', 'protein', 'sayur', 'buah', 'susu'];
// 3. Kirim ke view
return view('admin.rekomendasi', [
'rekomendasiByDate' => $rekomendasiByDate,
'tanggalList' => $tanggalList,
// Hitung total nilai_akhir per waktu & komponen
$totalPerGroup = [];
foreach ($rekomendasi as $r) {
$waktu = strtolower($r->waktuMakan->nama);
$komponen = strtolower($r->komponen->nama);
$totalPerGroup[$waktu][$komponen] = ($totalPerGroup[$waktu][$komponen] ?? 0) + $r->nilai_akhir;
}
// Group dan sort data
$rekomendasiGrouped = $rekomendasi
->groupBy([
fn($item) => strtolower($item->waktuMakan->nama),
fn($item) => strtolower($item->komponen->nama),
]);
foreach ($rekomendasiGrouped as $waktu => $komponens) {
$sorted = collect();
foreach ($urutanKomponen as $target) {
if ($komponens->has($target)) {
// Tambahkan properti persentase ke setiap item
$komponens[$target]->each(function ($item) use ($totalPerGroup, $waktu, $target) {
$total = $totalPerGroup[$waktu][$target] ?: 1;
$item->persentase = ($item->nilai_akhir / $total) * 100;
});
$sorted[$target] = $komponens[$target]->sortByDesc('nilai_akhir');
}
}
$rekomendasiGrouped[$waktu] = $sorted;
}
public function kirimKeUser($userId)
{
$adminId = Auth::id();
$dataAdmin = Rekomendasi::where('user_id', $adminId)->get();
foreach ($dataAdmin as $item) {
Rekomendasi::updateOrCreate(
['user_id' => $userId, 'makanan_id' => $item->makanan_id],
[
'nilai_akhir' => $item->nilai_akhir,
'tanggal_rekomendasi' => now(),
]
);
}
return redirect()->back()->with('success', 'Hasil rekomendasi berhasil dikirim ke user.');
return view('admin.rekomendasi', ['rekomendasi' => $rekomendasiGrouped]);
}
public function hapusSemua(Request $request)
{
$tanggal = $request->input('tanggal_rekomendasi');
if (!$tanggal) {
return redirect()->back()->with('error', 'Tanggal rekomendasi harus dipilih.');
}
// Hapus data berdasarkan tanggal_rekomendasi
Rekomendasi::where('tanggal_rekomendasi', $tanggal)->delete();
return redirect()->back()->with('success', 'Data rekomendasi untuk tanggal ' . $tanggal . ' berhasil dihapus.');
}
}

View File

@ -8,6 +8,8 @@
use App\Models\Makanan; // pastikan model di-import
use App\Models\JenisMakanan;
use App\Models\Kategori;
use App\Models\RekomendasiAhli;
use App\Models\WaktuMakan;
use Illuminate\Support\Facades\Auth; // pastikan ini ada di atas
class UserController extends Controller
@ -86,32 +88,34 @@ public function userdata(Request $request)
public function userresult(Request $request)
public function userresult()
{
$tanggal = $request->input('tanggal');
$rekomendasi = \App\Models\RekomendasiAhli::with('komponen', 'waktuMakan')
->get()
->groupBy([
fn($item) => $item->waktu_makan_id,
fn($item) => strtolower($item->komponen->nama),
]);
$rekomendasi = Rekomendasi::with('makanan')
->when($tanggal, function ($query) use ($tanggal) {
$query->whereDate('tanggal_rekomendasi', $tanggal);
})
$waktuMakans = \App\Models\WaktuMakan::all()->keyBy('id');
// Ambil alternatif dari tabel rekomendasis
$alternatifGrouped = \App\Models\Rekomendasi::with('makanan')
->where('nilai_akhir', '>', 0)
->orderByDesc('nilai_akhir')
->get();
->get()
->groupBy(fn($item) => $item->waktu_makan_id . '-' . $item->komponen_id);
$totalNilaiAkhir = $rekomendasi->sum('nilai_akhir');
// Ambil daftar tanggal unik dari database
$listTanggal = Rekomendasi::select('tanggal_rekomendasi')
->distinct()
->orderBy('tanggal_rekomendasi', 'desc')
->pluck('tanggal_rekomendasi');
return view('user.userresult', compact('rekomendasi', 'totalNilaiAkhir', 'tanggal', 'listTanggal'));
return view('user.userresult', compact('rekomendasi', 'waktuMakans', 'alternatifGrouped'));
}
public function show($id)
{
// ambil data berdasarkan $id atau bisa juga switch/case

25
app/Models/Komponen.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Komponen extends Model
{
use HasFactory;
protected $fillable = ['nama'];
public function makananKomponenWaktu()
{
return $this->hasMany(MakananKomponenWaktu::class);
}
public function makanans()
{
return $this->belongsToMany(Makanan::class, 'makanan_komponen_waktu')
->withPivot('waktu_makan_id')
->withTimestamps();
}
}

View File

@ -43,4 +43,25 @@ public function rekomendasi()
{
return $this->hasMany(Rekomendasi::class);
}
public function komponenWaktu()
{
return $this->hasMany(MakananKomponenWaktu::class);
}
public function komponens()
{
return $this->belongsToMany(Komponen::class, 'makanan_komponen_waktu')
->withPivot('waktu_makan_id')
->withTimestamps();
}
public function waktuMakans()
{
return $this->belongsToMany(WaktuMakan::class, 'makanan_komponen_waktu')
->withPivot('komponen_id')
->withTimestamps();
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MakananKomponenWaktu extends Model
{
use HasFactory;
protected $table = 'makanan_komponen_waktu';
protected $fillable = ['makanan_id', 'komponen_id', 'waktu_makan_id'];
public function makanan()
{
return $this->belongsTo(Makanan::class);
}
public function komponen()
{
return $this->belongsTo(Komponen::class);
}
public function waktuMakan()
{
return $this->belongsTo(WaktuMakan::class);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class PengecualianMakanan extends Model
{
use HasFactory;
protected $fillable = ['user_id', 'makanan_id', 'waktu_makan_id', 'komponen_id'];
public function makanan() {
return $this->belongsTo(Makanan::class);
}
public function waktuMakan() {
return $this->belongsTo(WaktuMakan::class);
}
public function komponen() {
return $this->belongsTo(Komponen::class);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class PreferensiWaktuKriteria extends Model
{
use HasFactory;
protected $table = 'preferensi_waktu_kriteria';
protected $fillable = [
'waktu_makan_id',
'kriteria_id',
'bobot',
];
/**
* Relasi ke model WaktuMakan
*/
public function waktuMakan()
{
return $this->belongsTo(WaktuMakan::class);
}
/**
* Relasi ke model Kriteria
*/
public function kriteria()
{
return $this->belongsTo(Kriteria::class);
}
}

View File

@ -9,7 +9,7 @@ class Rekomendasi extends Model
{
use HasFactory;
public $timestamps = false;
protected $fillable = ['user_id', 'makanan_id', 'nilai_akhir', 'tanggal_rekomendasi'];
protected $fillable = ['user_id', 'makanan_id', 'komponen_id', 'waktu_makan_id', 'nilai_akhir', 'tanggal_rekomendasi'];
public function user()
{
@ -20,4 +20,15 @@ public function makanan()
{
return $this->belongsTo(Makanan::class);
}
public function komponen()
{
return $this->belongsTo(Komponen::class);
}
public function waktuMakan()
{
return $this->belongsTo(WaktuMakan::class);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class RekomendasiAhli extends Model
{
use HasFactory;
protected $table = 'rekomendasi_ahli';
protected $fillable = [
'makanan_id',
'waktu_makan_id',
'komponen_id',
'catatan',
];
// Relasi ke makanan
public function makanan()
{
return $this->belongsTo(Makanan::class);
}
// Relasi ke waktu makan
public function waktuMakan()
{
return $this->belongsTo(WaktuMakan::class);
}
// Relasi ke komponen
public function komponen()
{
return $this->belongsTo(Komponen::class);
}
}

27
app/Models/WaktuMakan.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class WaktuMakan extends Model
{
use HasFactory;
protected $table = 'waktu_makans'; // custom table name
protected $fillable = ['nama', 'keterangan'];
public function makananKomponenWaktu()
{
return $this->hasMany(MakananKomponenWaktu::class);
}
public function makanans()
{
return $this->belongsToMany(Makanan::class, 'makanan_komponen_waktu')
->withPivot('komponen_id')
->withTimestamps();
}
}

View File

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

View File

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

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('makanan_komponen_waktu', function (Blueprint $table) {
$table->id();
$table->foreignId('makanan_id')->constrained('makanans')->onDelete('cascade');
$table->foreignId('komponen_id')->constrained('komponens')->onDelete('cascade');
$table->foreignId('waktu_makan_id')->constrained('waktu_makans')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('makanan_komponen_waktu');
}
};

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('rekomendasis', function (Blueprint $table) {
if (!Schema::hasColumn('rekomendasis', 'komponen_id')) {
$table->foreignId('komponen_id')->after('makanan_id')->constrained('komponens')->cascadeOnDelete();
}
if (!Schema::hasColumn('rekomendasis', 'waktu_makan_id')) {
$table->foreignId('waktu_makan_id')->after('komponen_id')->constrained('waktu_makans')->cascadeOnDelete();
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('rekomendasis', function (Blueprint $table) {
$table->dropForeign(['komponen_id']);
$table->dropForeign(['waktu_makan_id']);
$table->dropColumn(['komponen_id', 'waktu_makan_id']);
});
}
};

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('rekomendasi_ahli', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('makanan_id');
$table->unsignedBigInteger('waktu_makan_id');
$table->unsignedBigInteger('komponen_id');
$table->text('catatan')->nullable();
$table->timestamps();
// Foreign Keys
$table->foreign('makanan_id')->references('id')->on('makanans')->onDelete('cascade');
$table->foreign('waktu_makan_id')->references('id')->on('waktu_makans')->onDelete('cascade');
$table->foreign('komponen_id')->references('id')->on('komponens')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('rekomendasi_ahli');
}
};

View File

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('waktu_makans', function (Blueprint $table) {
$table->string('keterangan')->nullable()->after('nama');
});
}
public function down(): void
{
Schema::table('waktu_makans', function (Blueprint $table) {
$table->dropColumn('keterangan');
});
}
};

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Hapus foreign key dan ubah tipe kolom
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->dropForeign(['makanan_id']);
});
// Ubah kolom menjadi text (rename lalu tambahkan kembali)
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->dropColumn('makanan_id');
});
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->text('makanan_id')->after('id');
});
}
public function down(): void
{
// Revert: ubah kembali ke foreign key integer
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->dropColumn('makanan_id');
});
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->foreignId('makanan_id')->constrained('makanans')->onDelete('cascade');
});
}
};

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
try {
DB::statement('ALTER TABLE rekomendasi_ahli DROP FOREIGN KEY rekomendasi_ahli_makanan_id_foreign');
} catch (\Throwable $e) {
// Abaikan jika FK tidak ada
}
// 2. Hapus kolom makanan_id jika masih ada
if (Schema::hasColumn('rekomendasi_ahli', 'makanan_id')) {
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->dropColumn('makanan_id');
});
}
// 3. Tambahkan nama_makanan hanya jika belum ada
if (!Schema::hasColumn('rekomendasi_ahli', 'nama_makanan')) {
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->text('nama_makanan')->after('id');
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Revert ke kondisi sebelumnya
Schema::table('rekomendasi_ahli', function (Blueprint $table) {
$table->dropColumn('nama_makanan');
$table->unsignedBigInteger('makanan_id')->nullable();
$table->foreign('makanan_id')->references('id')->on('makanans')->onDelete('cascade');
});
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('preferensi_waktu_kriteria', function (Blueprint $table) {
$table->id();
$table->foreignId('waktu_makan_id')->constrained()->onDelete('cascade');
$table->foreignId('kriteria_id')->constrained()->onDelete('cascade');
$table->float('bobot'); // nilai 01
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('preferensi_waktu_kriteria');
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('pengecualian_makanans', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('makanan_id')->constrained('makanans')->onDelete('cascade');
$table->foreignId('waktu_makan_id')->constrained('waktu_makans')->onDelete('cascade');
$table->foreignId('komponen_id')->constrained('komponens')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('pengecualian_makanans');
}
};

View File

@ -66,44 +66,41 @@
</form>
{{-- Form Pemilihan Alternatif --}}
<form action="{{ route('alternatif.pilih.simpan') }}" method="POST">
@csrf
<div class="row">
@foreach ($makanans as $makanan)
<div class="col-12 col-sm-6 col-lg-4 mb-4">
<div class="card h-100 border-0 shadow-sm hover-shadow" style="transition: 0.3s;">
<div class="card-body d-flex align-items-start gap-2">
<input
class="form-check-input mt-1 alternatif-checkbox"
type="checkbox"
name="alternatifs[]"
value="{{ $makanan->id }}"
id="alt{{ $makanan->id }}"
style="transform: scale(1.2);"
>
<label class="form-check-label ms-2 w-100" for="alt{{ $makanan->id }}">
<h6 class="fw-bold mb-1">{{ $makanan->nama }}</h6>
<ul class="list-unstyled small text-muted mb-0">
<li><i class="bi bi-droplet-half me-1"></i> Lemak: {{ $makanan->lemak }} g</li>
<li><i class="bi bi-shield-check me-1"></i> Natrium: {{ $makanan->natrium }} mg</li>
<li><i class="bi bi-lightning me-1"></i> Energi: {{ $makanan->energi }} kal</li>
<li><i class="bi bi-activity me-1"></i> Karbohidrat: {{ $makanan->karbohidrat }} g</li>
</ul>
</label>
</div>
</div>
</div>
@endforeach
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-success btn-lg px-4 mt-3" id="submitBtn" disabled>
<i class="bi bi-arrow-right-circle-fill me-1"></i> Lanjutkan
</button>
</div>
</form>
@csrf
<div class="row">
@foreach ($makanans as $makanan)
<div class="col-12 col-sm-6 col-lg-4 mb-4">
<div class="card h-100 border-0 shadow-sm hover-shadow" style="transition: 0.3s;">
<div class="card-body d-flex align-items-start gap-2">
<input
class="form-check-input mt-1 alternatif-checkbox"
type="checkbox"
name="alternatifs[]"
value="{{ $makanan->id }}"
id="alt{{ $makanan->id }}"
style="transform: scale(1.2);"
>
<label class="form-check-label ms-2 w-100" for="alt{{ $makanan->id }}">
<h6 class="fw-bold mb-1">{{ $makanan->nama }}</h6>
<ul class="list-unstyled small text-muted mb-0">
<li><i class="bi bi-droplet-half me-1"></i> Lemak: {{ $makanan->lemak }} g</li>
<li><i class="bi bi-shield-check me-1"></i> Natrium: {{ $makanan->natrium }} mg</li>
<li><i class="bi bi-lightning me-1"></i> Energi: {{ $makanan->energi }} kal</li>
<li><i class="bi bi-activity me-1"></i> Karbohidrat: {{ $makanan->karbohidrat }} g</li>
</ul>
</label>
</div>
</div>
</div>
@endforeach
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-success btn-lg px-4 mt-3" id="submitBtn" disabled>
<i class="bi bi-arrow-right-circle-fill me-1"></i> Lanjutkan
</button>
</div>
</form>
<hr class="my-4">
</div>
</div>

View File

@ -0,0 +1,57 @@
@extends('layout.app')
@section('content')
<div class="pagetitle">
<h1>Data komponen</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('admindash') }}">Home</a></li>
<li class="breadcrumb-item active"><a href="{{ route('komponen') }}"> komponen</a></li>
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-2">Data komponen</h5>
{{-- <div class="text-center">
<a href="{{ route('tambahkomponen') }}" class="btn btn-primary">Tambah</a>
</div> --}}
</div>
<!-- Table with stripped rows -->
<table class="table datatable">
<thead>
<tr>
<th class="text-center">No</th>
<th class="text-center">Name</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach ($komponens as $kmpn)
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center">{{ $kmpn->nama }}</td>
<td class="text-center">
{{-- <a href="{{ route('edituser', $usr->id) }}" class="btn btn-sm btn-warning">Edit</a>
<form action="{{ route('deleteuser', $usr->id) }}" method="POST" class="d-inline delete-form">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger delete-button">Hapus</button>
</form> --}}
</td>
</tr>
@endforeach
</tbody>
</table>
<!-- End Table with stripped rows -->
</div>
</div>
</div>
</div>
</section>
@endsection

View File

@ -14,14 +14,22 @@
<section class="section profile">
<div class="row">
<!-- Kartu info ringkas -->
<div class="col-xl-4">
<div class="card">
<div class="card-body pt-4 text-center">
<h2>{{ $user->name }}</h2>
<h5 class="text-muted mb-0">{{ $user->role->name ?? '-' }}</h5>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card border-0 shadow-sm text-center p-4 rounded-4">
<div class="mb-3">
<div class="d-flex justify-content-center">
<div class="rounded-circle bg-white shadow p-4 d-flex align-items-center justify-content-center" style="width: 100px; height: 100px;">
<i class="bi bi-person-fill text-primary fs-1"></i>
</div>
</div>
</div>
<h5 class="fw-semibold mb-1">{{ $user->name }}</h5>
<p class="text-muted mb-2">{{ $user->email }}</p>
<span class="badge rounded-pill bg-primary">
<i class="bi bi-award me-1"></i> {{ ucfirst($user->role->name ?? 'user') }}
</span>
</div>
</div>
<!-- Tabs -->
<div class="col-xl-8">

View File

@ -8,16 +8,16 @@
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('admindash') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('perbandingan') }}">Perbandingan Kriteria</a></li>
<li class="breadcrumb-item"><a href="{{ route('hasil.normalisasi') }}">Normalisasi Kriteria</a></li>
<li class="breadcrumb-item active">Normalisasi Kriteria</li>
</ol>
</nav>
</div><!-- End Page Title -->
</div>
<section class="section">
<div class="row">
<div class="col-lg-12">
<!-- Form Input Normalisasi -->
<!-- Matriks Normalisasi -->
<div class="card">
<div class="card-body">
<h5 class="card-title mb-4">Normalisasi Matriks</h5>
@ -36,9 +36,7 @@
<tr>
<th>{{ $baris->nama }}</th>
@foreach ($kriterias as $j => $kolom)
<td>
{{ number_format($normalisasi[$i][$j], 4) }}
</td>
<td>{{ number_format($normalisasi[$i][$j], 4) }}</td>
@endforeach
</tr>
@endforeach
@ -48,7 +46,7 @@
</div>
</div>
<!-- Tabel Bobot Kriteria -->
<!-- Bobot Kriteria -->
<div class="card mt-4">
<div class="card-body">
<h5 class="card-title">Bobot Kriteria</h5>
@ -73,12 +71,11 @@
</div>
</div>
<!-- Tindakan Selanjutnya -->
<div class="text-end mt-3">
<form method="GET" action="{{ route('alternatif.pilih') }}">
@csrf
<button type="submit" class="btn btn-success">Lanjut ke Perbandingan Alternatif</button>
</form>
<!-- Tombol Proses Otomatis -->
<div class="text-end mt-4">
<a href="{{ route('rekomendasi.hitung.otomatis') }}" class="btn btn-success btn-lg">
<i class="bi bi-lightning-fill me-1"></i> Proses Rekomendasi Otomatis
</a>
</div>
</div>

View File

@ -1,135 +1,102 @@
@extends('layout.app')
@section('content')
<div class="container mt-4">
<div class="pagetitle">
<h1>🏆 Ranking Rekomendasi Makanan</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('admindash') }}">Home</a></li>
<li class="breadcrumb-item active">Hasil Rekomendasi</li>
</ol>
</nav>
</div>
{{-- Judul halaman --}}
<div class="text-center mb-4">
<h2 class="fw-bold">📊 Hasil Rekomendasi Makanan</h2>
<p class="text-muted">Berikut adalah peringkat makanan berdasarkan hasil perhitungan AHP.</p>
</div>
<section class="section">
<div class="row">
<div class="col-12">
{{-- Flash success --}}
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
@if(session('success'))
<div class="alert alert-success shadow-sm rounded-3">
<i class="bi bi-check-circle me-2"></i> {{ session('success') }}
</div>
@endif
{{-- ===================== CAROUSEL ===================== --}}
<div id="rekomendasiCarousel"
class="carousel slide"
data-bs-touch="true"
data-bs-ride="carousel"
data-bs-interval="7000"> {{-- non-auto slide, user geser manual --}}
<div class="carousel-inner">
@forelse ($rekomendasi as $waktu => $komponens)
<div class="card shadow-lg border-start border-4 border-success mt-4">
<div class="card-body">
<h4 class="card-title fw-bold text-success mb-4">
🍽️ Menu {{ ucfirst($waktu) }}
</h4>
@foreach($rekomendasiByDate as $tanggal => $items)
<div class="carousel-item {{ $loop->first ? 'active' : '' }}">
<div class="card shadow rounded">
<div class="card-header bg-success text-white text-center">
<h5 class="mb-0">
Rekomendasi&nbsp;
{{ \Carbon\Carbon::parse($tanggal)->translatedFormat('d F Y') }}
</h5>
</div>
@foreach ($komponens as $komponen => $items)
@php
$utama = $items->first();
$alternatif = $items->skip(1)->take(4)->values(); // top 5 total
@endphp
<div class="card-body p-0">
<table class="table table-hover mb-0 text-center">
<thead class="table-light">
<tr>
<th style="width: 15%">🏅 Peringkat</th>
<th>🍽️ Nama Makanan</th>
<th style="width: 25%">📈 Nilai Akhir</th>
</tr>
</thead>
<tbody>
@foreach($items as $index => $item)
<tr>
<td>
<span class="badge bg-primary fs-6">
{{ $index + 1 }}
</span>
</td>
<td class="fw-semibold">{{ $item->makanan->nama }}</td>
<td>{{ number_format($item->nilai_akhir, 4) }}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mb-4">
<h5 class="text-primary fw-semibold">
📌 {{ ucfirst($komponen) }}
</h5>
<div class="border rounded-3 p-3 bg-light shadow-sm mb-2 d-flex justify-content-between align-items-center">
<div>
🌟 <strong>{{ $utama->makanan->nama }}</strong>
</div>
<span class="badge bg-success rounded-pill">
{{ number_format($utama->persentase, 2) }}%
</span>
</div>
@if ($alternatif->count() > 0)
<button class="btn btn-sm btn-outline-primary toggle-btn mb-2"
type="button"
data-target="#alt-{{ $waktu }}-{{ $komponen }}">
🔁 Lihat Alternatif
</button>
<ol id="alt-{{ $waktu }}-{{ $komponen }}"
class="list-group list-group-numbered shadow-sm d-none">
@foreach ($alternatif as $index => $alt)
<li class="list-group-item d-flex justify-content-between align-items-center">
<div>
@if($index == 0) 🥈 @elseif($index == 1) 🥉 @else 🔹 @endif
<strong>{{ $alt->makanan->nama }}</strong>
</div>
<span class="badge bg-secondary rounded-pill">
{{ number_format($alt->persentase, 2) }}%
</span>
</li>
@endforeach
</ol>
@endif
</div>
@endforeach
</div>
</div>
</div>
@endforeach
@empty
<div class="alert alert-warning mt-4">
<i class="bi bi-exclamation-circle me-2"></i> Tidak ada data rekomendasi tersedia untuk hari ini.
</div>
@endforelse
</div>
{{-- Navigasi kiri / kanan --}}
<button class="carousel-control-prev" type="button"
data-bs-target="#rekomendasiCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button"
data-bs-target="#rekomendasiCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
<span class="visually-hidden">Next</span>
</button>
{{-- Indikator (bulatan) --}}
<div class="carousel-indicators position-relative mt-3">
@foreach($rekomendasiByDate as $tanggal => $items)
<button type="button"
data-bs-target="#rekomendasiCarousel"
data-bs-slide-to="{{ $loop->index }}"
class="{{ $loop->first ? 'active' : '' }}"
aria-label="Slide {{ $loop->iteration }}"></button>
@endforeach
</div>
</div>
{{-- ================== AKHIR CAROUSEL ================== --}}
</section>
@endsection
{{-- Tombol kirim hasil --}}
<div class="mt-4 text-end">
<form action="{{ route('rekomendasi.kirim', ['user' => 2]) }}"
method="POST"
onsubmit="return confirm('Yakin ingin mengirim hasil ini ke user?')">
@csrf
<button type="submit" class="btn btn-success">
📤 Kirim Hasil ke User
</button>
</form>
</div>
{{-- Form hapus per tanggal --}}
<div class="mt-3 text-end">
<form action="{{ route('rekomendasi.hapusSemua') }}"
method="POST"
class="d-inline-flex align-items-center gap-2">
@csrf
<label class="mb-0 fw-semibold" for="tanggal_rekomendasi">
Pilih Tanggal Rekomendasi:
</label>
<select name="tanggal_rekomendasi" id="tanggal_rekomendasi" required class="form-select w-auto">
<option value="">-- Pilih Tanggal --</option>
@foreach ($tanggalList as $tgl)
<option value="{{ $tgl }}">
{{ \Carbon\Carbon::parse($tgl)->format('d M Y') }}
</option>
@endforeach
</select>
<button type="submit" class="btn btn-danger">
Hapus Data
</button>
</form>
</div>
{{-- Tombol kembali --}}
<div class="text-end mt-4">
<a href="{{ route('alternatif.pilih') }}" class="btn btn-outline-secondary">
🔙 Kembali ke Pemilihan Alternatif
</a>
</div>
</div>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.toggle-btn').forEach(button => {
button.addEventListener('click', function () {
const target = document.querySelector(this.dataset.target);
target.classList.toggle('d-none');
this.textContent = target.classList.contains('d-none')
? '🔁 Lihat Alternatif'
: '🔼 Sembunyikan Alternatif';
});
});
});
</script>
@endpush

View File

@ -0,0 +1,52 @@
@extends('layout.app')
@section('content')
<div class="container">
<h2 class="mb-4">Edit Relasi</h2>
@if(session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif
<form method="POST" action="{{ route('relasi.update', $relasi->id) }}">
@csrf
@method('PUT')
<div class="form-group mb-3">
<label for="makanan_id">Makanan</label>
<select name="makanan_id" class="form-control">
@foreach($makanans as $makanan)
<option value="{{ $makanan->id }}" {{ $relasi->makanan_id == $makanan->id ? 'selected' : '' }}>
{{ $makanan->nama }}
</option>
@endforeach
</select>
</div>
<div class="form-group mb-3">
<label for="komponen_id">Komponen</label>
<select name="komponen_id" class="form-control">
@foreach($komponens as $komponen)
<option value="{{ $komponen->id }}" {{ $relasi->komponen_id == $komponen->id ? 'selected' : '' }}>
{{ $komponen->nama }}
</option>
@endforeach
</select>
</div>
<div class="form-group mb-3">
<label for="waktu_makan_id">Waktu Makan</label>
<select name="waktu_makan_id" class="form-control">
@foreach($waktuMakans as $waktu)
<option value="{{ $waktu->id }}" {{ $relasi->waktu_makan_id == $waktu->id ? 'selected' : '' }}>
{{ $waktu->nama }}
</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-success">Simpan Perubahan</button>
<a href="{{ route('relasi') }}" class="btn btn-secondary">Batal</a>
</form>
</div>
@endsection

View File

@ -0,0 +1,143 @@
@extends('layout.app')
@section('content')
<div class="container">
<h2 class="mb-4">Tambah Relasi Makanan - Komponen - Waktu Makan</h2>
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@elseif(session('error'))
<div class="alert alert-danger">{{ session('error') }}</div>
@endif
<form method="POST" action="{{ route('relasi.store') }}" class="mb-4">
@csrf
<div class="form-group mb-2">
<label for="makanan_id">Makanan</label>
<select name="makanan_id" class="form-control">
@foreach($makanans as $makanan)
<option value="{{ $makanan->id }}">{{ $makanan->nama }}</option>
@endforeach
</select>
</div>
<div class="form-group mb-2">
<label for="komponen_id">Komponen</label>
<select name="komponen_id" class="form-control">
@foreach($komponens as $komponen)
<option value="{{ $komponen->id }}">{{ $komponen->nama }}</option>
@endforeach
</select>
</div>
<div class="form-group mb-3">
<label for="waktu_makan_id">Waktu Makan</label>
<select name="waktu_makan_id" class="form-control">
@foreach($waktuMakans as $waktu)
<option value="{{ $waktu->id }}">{{ $waktu->nama }}</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-primary">Simpan Relasi</button>
</form>
<section class="section">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Filter Relasi</h5>
<form method="GET" action="{{ route('relasi') }}" class="row g-3 align-items-end mb-4">
<div class="col-md-4">
<label for="komponen_id" class="form-label">Filter Komponen</label>
<select name="komponen_id" class="form-select">
<option value="">-- Semua Komponen --</option>
@foreach($komponens as $komponen)
<option value="{{ $komponen->id }}" {{ request('komponen_id') == $komponen->id ? 'selected' : '' }}>
{{ $komponen->nama }}
</option>
@endforeach
</select>
</div>
<div class="col-md-4">
<label for="waktu_makan_id" class="form-label">Filter Waktu Makan</label>
<select name="waktu_makan_id" class="form-select">
<option value="">-- Semua Waktu Makan --</option>
@foreach($waktuMakans as $waktu)
<option value="{{ $waktu->id }}" {{ request('waktu_makan_id') == $waktu->id ? 'selected' : '' }}>
{{ $waktu->nama }}
</option>
@endforeach
</select>
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary">Terapkan Filter</button>
</div>
</form>
<hr>
<div class="d-flex justify-content-between align-items-center mb-2">
<h5 class="card-title mb-0">Data Relasi Tersimpan</h5>
</div>
<!-- Table with stripped rows -->
<table class="table datatable">
<thead>
<tr>
<th class="text-center">No</th>
<th class="text-center">Makanan</th>
<th class="text-center">Komponen</th>
<th class="text-center">Waktu Makanan</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach ($relasis as $relasi)
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center">{{ $relasi->makanan->nama }}</td>
<td class="text-center">{{ $relasi->komponen->nama }}</td>
<td class="text-center">{{ $relasi->waktuMakan->nama }}</td>
<td class="text-center">
<a href="{{ route('relasi.edit', $relasi->id) }}" class="btn btn-warning btn-sm" title="Edit">
<i class="fas fa-edit"></i>
</a>
<form action="{{ route('relasi.destroy', $relasi->id) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus?');" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn btn-danger btn-sm" title="Hapus">
<i class="fas fa-trash-alt"></i>
</button>
</form>
</td>
{{-- <td class="text-center">
<form action="{{ route('relasi.destroy', $relasi->id) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus?');" style="display:inline;">
@csrf
@method('DELETE')
<button class="btn btn-danger btn-sm" title="Hapus">
<i class="fas fa-trash-alt"></i>
</button>
</form>
</td> --}}
</tr>
@endforeach
</tbody>
</table>
<!-- End Table -->
</div>
</div>
</div>
</div>
</section>
</div>
@endsection

View File

@ -0,0 +1,57 @@
@extends('layout.app')
@section('content')
<div class="pagetitle">
<h1>Data waktu makan</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('admindash') }}">Home</a></li>
<li class="breadcrumb-item active"><a href="{{ route('waktumakan') }}"> waktumakan</a></li>
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<h5 class="card-title mb-2">Data komponen</h5>
{{-- <div class="text-center">
<a href="{{ route('tambahkomponen') }}" class="btn btn-primary">Tambah</a>
</div> --}}
</div>
<!-- Table with stripped rows -->
<table class="table datatable">
<thead>
<tr>
<th class="text-center">No</th>
<th class="text-center">Name</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
@foreach ($waktu_makans as $wm)
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center">{{ $wm ->nama }}</td>
<td class="text-center">
{{-- <a href="{{ route('edituser', $usr->id) }}" class="btn btn-sm btn-warning">Edit</a>
<form action="{{ route('deleteuser', $usr->id) }}" method="POST" class="d-inline delete-form">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger delete-button">Hapus</button>
</form> --}}
</td>
</tr>
@endforeach
</tbody>
</table>
<!-- End Table with stripped rows -->
</div>
</div>
</div>
</div>
</section>
@endsection

View File

@ -12,6 +12,8 @@
<!-- Favicons -->
<link rel ="icon" type="image" href="{{ asset('logo/baru/dutdut.png') }}">
<link rel ="icon" type="image" href ="{{ asset('logo/baru/dutdut.png') }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<!-- Google Fonts -->
@ -28,6 +30,7 @@
<link href="{{ asset('assets/vendor/quill/quill.bubble.css') }}" rel="stylesheet">
<link href="{{ asset('assets/vendor/remixicon/remixicon.css') }}" rel="stylesheet">
<link href="{{ asset('assets/vendor/simple-datatables/style.css') }}" rel="stylesheet">
<!-- Template Main CSS File -->
<link href="{{ asset('assets/css/style.css') }}" rel="stylesheet">

View File

@ -1,10 +1,10 @@
<div class="copyright">
{{-- &copy; Copyright <strong><span>NiceAdmin</span></strong>. All Rights Reserved --}}
&copy; Copyright <strong><span>HeartChoice</span></strong>. All Rights Reserved
</div>
<div class="credits">
<!-- All the links in the footer should remain intact. -->
<!-- You can delete the links only if you purchased the pro version. -->
<!-- Licensing information: https://bootstrapmade.com/license/ -->
<!-- Purchase the pro version with working PHP/AJAX contact form: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ -->
{{-- Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a> --}}
Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a>
</div>

View File

@ -10,6 +10,14 @@
<li class="nav-heading">Pages</li>
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('datauser') ? '' : 'collapsed' }}" href="{{ route('datauser') }}">
<i class="ri-admin-line"></i>
<span>User</span>
</a>
</li><!-- End Profile Page Nav -->
{{-- <li class="nav-item">
<a class="nav-link {{ request()->routeIs(['tables-general', 'tables-data']) ? '' : 'collapsed' }}" data-bs-target="#users-nav" data-bs-toggle="collapse" href="#">
<i class="ri-admin-line"></i><span>User</span><i class="bi bi-chevron-down ms-auto"></i>
</a>
@ -20,14 +28,9 @@
<span>Role</span>
</a>
</li>
<li>
<a class="nav-link {{ request()->routeIs('datauser') ? '' : 'collapsed' }}" href="{{ route('datauser') }}">
<i class="ri-store-2-line"></i>
<span>User</span>
</a>
</li>
</ul>
</li>
</li> --}}
<li class="nav-item">
<a class="nav-link {{ request()->routeIs(['tables-general', 'tables-data']) ? '' : 'collapsed' }}" data-bs-target="#foods-nav" data-bs-toggle="collapse" href="#">
@ -52,6 +55,24 @@
<span>Data Makanan</span>
</a>
</li>
<li>
<a class="nav-link {{ request()->routeIs('komponen') ? '' : 'collapsed' }}" href="{{ route('komponen') }}">
<i class="ri-store-2-line"></i>
<span>Data komponen</span>
</a>
</li>
<li>
<a class="nav-link {{ request()->routeIs('waktumakan') ? '' : 'collapsed' }}" href="{{ route('waktumakan') }}">
<i class="ri-store-2-line"></i>
<span>waktu makan</span>
</a>
</li>
<li>
<a class="nav-link {{ request()->routeIs('relasi') ? '' : 'collapsed' }}" href="{{ route('relasi') }}">
<i class="ri-store-2-line"></i>
<span>relasi</span>
</a>
</li>
</ul>
</li>

View File

@ -3,99 +3,115 @@
@section('content')
<div class="container-xxl py-5">
<div class="container">
<div class="text-center mx-auto mb-5 wow fadeInUp" data-wow-delay="0.1s" style="max-width: 600px;">
<p class="d-inline-block border rounded-pill py-1 px-4 bg-primary text-white">Hasil Rekomendasi AHP</p>
<h1 class="mb-3">Rekomendasi Menu Makanan Terbaik untuk Anda</h1>
<p class="text-muted">Berikut adalah hasil akhir dari perhitungan berdasarkan metode AHP. Nilai akhir menunjukkan tingkat kesesuaian berdasarkan kriteria yang telah di pilih.</p>
<form method="GET" action="{{ route('userresult') }}" class="row g-3 mb-4 justify-content-center">
<div class="col-md-auto">
<select name="tanggal" class="form-select" onchange="this.form.submit()">
<option value="">-- Pilih Tanggal Rekomendasi --</option>
@foreach ($listTanggal as $tgl)
<option value="{{ $tgl }}" {{ request('tanggal') == $tgl ? 'selected' : '' }}>
{{ \Carbon\Carbon::parse($tgl)->translatedFormat('d F Y') }}
</option>
@endforeach
</select>
</div>
<div class="col-md-auto">
<a href="{{ route('userresult') }}" class="btn btn-secondary">Reset</a>
</div>
</form>
<div class="text-center mx-auto mb-5" style="max-width: 700px;">
<p class="d-inline-block border rounded-pill py-1 px-4 bg-primary text-white">Hasil Rekomendasi Ahli</p>
<h1 class="mb-3">Menu Makanan Terbaik dari Ahli Gizi</h1>
<p class="text-muted">Ditampilkan berdasarkan panduan ahli gizi untuk penderita penyakit jantung.</p>
</div>
<div class="table-responsive wow fadeInUp" data-wow-delay="0.2s">
<table class="table align-middle table-hover shadow-sm">
<thead class="table-dark text-center">
<tr>
<th>Peringkat</th>
<th>Nama Makanan</th>
<th>Nilai Akhir</th>
<th>Persentase</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($rekomendasi->sortByDesc('nilai_akhir')->values() as $index => $rekom)
<tr>
<td class="text-center">
<span class="badge bg-success fs-6">{{ $index + 1 }}</span>
</td>
<td class="fw-semibold">{{ $rekom->makanan->nama }}</td>
<td class="text-center">{{ number_format($rekom->nilai_akhir, 4) }}</td>
<td style="min-width: 150px;">
@forelse ($rekomendasi as $waktu => $komponens)
<div class="card shadow-sm border-start border-4 border-primary mb-5">
<div class="card-body">
<h4 class="text-primary fw-bold mb-2">
🍽️ Menu {{ $waktuMakans[$waktu]->nama ?? ucfirst($waktu) }}
</h4>
@if($waktuMakans[$waktu]?->keterangan)
<p class="text-muted fst-italic mb-3">
🕒 {{ $waktuMakans[$waktu]->keterangan }}
</p>
@endif
<div class="d-flex overflow-auto gap-4 pb-2" style="scroll-snap-type: x mandatory;">
@foreach ($komponens as $komponen => $items)
@php
$persentase = ($totalNilaiAkhir > 0) ? ($rekom->nilai_akhir / $totalNilaiAkhir) * 100 : 0;
$utama = $items->first();
$utamaNama = $utama->nama_makanan ?? '-';
$utamaPersen = 100;
$modalId = 'modal-' . Str::slug($waktu . '-' . $komponen);
// Ambil alternatif dari tabel rekomendasis
$alternatifKey = $waktu . '-' . $utama->komponen_id;
$alternatifList = $alternatifGrouped[$alternatifKey] ?? collect();
@endphp
<div class="d-flex align-items-center">
<div class="progress flex-grow-1 me-2" style="height: 20px;">
<div class="card shadow-sm p-3 bg-light" style="min-width: 300px; scroll-snap-align: start;">
<h6 class="fw-bold text-secondary mb-2">{{ ucfirst($komponen) }}</h6>
<div class="d-flex justify-content-between align-items-center mb-2">
<span>🌟 <strong>{{ $utamaNama }}</strong></span>
<span class="badge bg-success">{{ number_format($utamaPersen, 2) }}%</span>
</div>
<div class="progress mb-2" style="height: 16px;">
<div class="progress-bar bg-info" role="progressbar"
style="width: {{ $persentase }}%;"
aria-valuenow="{{ $persentase }}" aria-valuemin="0" aria-valuemax="100">
style="width: {{ $utamaPersen }}%;"
aria-valuenow="{{ $utamaPersen }}" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
<small class="fw-semibold text-dark" style="width: 50px;">{{ number_format($persentase, 2) }}%</small>
@if ($alternatifList->count() > 0)
<button class="btn btn-sm btn-outline-primary w-100 mt-2"
data-bs-toggle="modal" data-bs-target="#{{ $modalId }}">
🔁 Lihat Alternatif
</button>
@endif
</div>
</td>
<td class="text-center">
<a class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#detailModal{{ $index }}">
<i class="fa fa-info-circle me-1"></i> Detail
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Modal Alternatif -->
<div class="modal fade" id="{{ $modalId }}" tabindex="-1" aria-labelledby="{{ $modalId }}Label" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content shadow">
<div class="modal-header">
<h5 class="modal-title">Alternatif untuk {{ ucfirst($komponen) }} ({{ $waktuMakans[$waktu]->nama }})</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Tutup"></button>
</div>
<div class="modal-body">
@foreach ($alternatifList->sortByDesc('nilai_akhir')->take(4) as $index => $alt)
@php $persen = $alt->nilai_akhir * 100; @endphp
<div class="d-flex justify-content-between align-items-center mb-2">
<span>
@if ($index == 0) 🥈 @elseif ($index == 1) 🥉 @else 🔹 @endif
{{ $alt->makanan->nama }}
</span>
<span class="badge bg-secondary">{{ number_format($persen, 2) }}%</span>
</div>
<div class="progress mb-2" style="height: 14px;">
<div class="progress-bar bg-secondary" role="progressbar"
style="width: {{ $persen }}%;"
aria-valuenow="{{ $persen }}" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
@endforeach
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<!-- End Modal -->
@endforeach
</div>
</div>
</div>
@empty
<div class="alert alert-warning text-center">
<i class="fa fa-exclamation-circle me-2"></i> Tidak ada hasil rekomendasi ahli tersedia saat ini.
</div>
@endforelse
</div>
</div>
@foreach ($rekomendasi as $index => $rekom)
<!-- Modal -->
<div class="modal fade" id="detailModal{{ $index }}" tabindex="-1" aria-labelledby="detailModalLabel{{ $index }}" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="detailModalLabel{{ $index }}">Detail Makanan: {{ $rekom->makanan->nama }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Tutup"></button>
</div>
<div class="modal-body">
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Lemak:</strong> {{ $rekom->makanan->lemak }} g</li>
<li class="list-group-item"><strong>Natrium:</strong> {{ $rekom->makanan->natrium }} mg</li>
<li class="list-group-item"><strong>Energi:</strong> {{ $rekom->makanan->energi }} kal</li>
<li class="list-group-item"><strong>Karbohidrat:</strong> {{ $rekom->makanan->karbohidrat }} g</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
@endforeach
@endsection
@push('styles')
<style>
.card-carousel::-webkit-scrollbar {
display: none;
}
.card-carousel {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
@endpush

View File

@ -12,6 +12,7 @@
use App\Http\Controllers\RegisterController;
use App\Http\Controllers\RekomendasiController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\MenuAHPController;
/*
|--------------------------------------------------------------------------
@ -85,6 +86,32 @@
Route::delete('/makanan/{makanan}', [MakananController::class, 'deletemakanan'])->name('deletemakanan');
Route::get('/tambahmakanan', [MakananController::class, 'tambahmakanan'])->name('tambahmakanan');
Route::post('/makanan/storemakanan', [MakananController::class, 'storemakanan'])->name('makanan.storemakanan');
// Routes untuk komponen
Route::get('/komponen', [MakananController::class, 'komponen'])->name('komponen');
Route::get('/editkomponen/{komponen}/editkomponen', [MakananController::class, 'editkomponen'])->name('editkomponen');
Route::put('/komponen/{komponen}', [MakananController::class, 'updatekomponen'])->name('updatekomponen');
Route::delete('/komponen/{komponen}', [MakananController::class, 'deletekomponen'])->name('deletekomponen');
Route::get('/tambahkomponen', [MakananController::class, 'tambahkomponen'])->name('tambahkomponen');
Route::post('/komponen/storekomponen', [MakananController::class, 'storekomponen'])->name('makanan.storekomponen');
// Routes untuk waktu makan
Route::get('/waktumakan', [MakananController::class, 'waktumakan'])->name('waktumakan');
Route::get('/editwaktumakan/{waktumakan}/editwaktumakan', [MakananController::class, 'editwaktumakan'])->name('editwaktumakan');
Route::put('/waktumakan/{waktumakan}', [MakananController::class, 'updatewaktumakan'])->name('updatewaktumakan');
Route::delete('/waktumakan/{waktumakan}', [MakananController::class, 'deletewaktumakan'])->name('deletewaktumakan');
Route::get('/tambahwaktumakan', [MakananController::class, 'tambahwaktumakan'])->name('tambahwaktumakan');
Route::post('/waktumakan/storewaktumakan', [MakananController::class, 'storewaktumakan'])->name('makanan.storewaktumakan');
Route::get('/relasi', [MakananController::class, 'index'])->name('relasi');
Route::post('/relasi/storerelasi', [MakananController::class, 'store'])->name('relasi.store');
Route::delete('/relasi/{relasi}', [MakananController::class, 'destroy'])->name('relasi.destroy');
Route::get('/relasi/{id}/edit', [MakananController::class, 'edit'])->name('relasi.edit');
Route::put('/relasi/{id}', [MakananController::class, 'update'])->name('relasi.update');
});
// Routes untuk kriteria
@ -117,10 +144,8 @@
Route::post('/alternatif/simpan-normalisasi', [AlternatifController::class, 'simpanNormalisasi'])->name('alternatif.simpanNormalisasi');
// Routes untuk rekomendasi
Route::get('/rekomendasi/proses', [RekomendasiController::class, 'hitungDanSimpan'])->name('rekomendasi.proses');
Route::get('/rekomendasi/hasil', [RekomendasiController::class, 'tampil'])->name('rekomendasi.hasil');
Route::post('/rekomendasi/kirim/{user}', [RekomendasiController::class, 'kirimKeUser'])->name('rekomendasi.kirim');
Route::delete('/rekomendasi/hapus', [RekomendasiController::class, 'hapusSemua'])->name('rekomendasi.hapusSemua');
Route::get('/rekomendasi/hitung-otomatis', [RekomendasiController::class, 'hitungDanSimpanOtomatis'])->name('rekomendasi.hitung.otomatis');
Route::get('/rekomendasi/hasil', [RekomendasiController::class, 'tampilHasil'])->name('rekomendasi.hasil');
});
@ -129,6 +154,7 @@
// User Routes (Hanya bisa diakses oleh User)
Route::middleware(['cekrole:user'])->group(function () {
Route::get('/userdash', [UserController::class, 'userdash'])->name('userdash');