483 lines
15 KiB
PHP
483 lines
15 KiB
PHP
@extends('layout.app')
|
|
|
|
@section('title', 'Daftar Rekomendasi')
|
|
|
|
@include('admin.shared.admin-styles')
|
|
|
|
@section('content')
|
|
<div class="admin-container container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="page-header animate-fade-in">
|
|
<div class="row align-items-center">
|
|
<div class="col-12">
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3">
|
|
<div>
|
|
<h3 class="mb-1 text-white d-flex align-items-center">
|
|
<i class="fas fa-list-alt me-2"></i>Daftar Rekomendasi Makanan
|
|
</h3>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{{ route('admindash') }}" class="text-white-50">Dashboard</a></li>
|
|
<li class="breadcrumb-item active text-white" aria-current="page">Daftar Rekomendasi</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button class="admin-btn btn-primary" onclick="window.location.reload()">
|
|
<i class="fas fa-sync-alt me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Overview -->
|
|
<div class="stats-overview animate-fade-in">
|
|
<div class="row g-3">
|
|
<div class="col-12 col-md-4">
|
|
<div class="stat-card bg-gradient-success">
|
|
<div class="stat-icon">
|
|
<i class="fas fa-check-circle"></i>
|
|
</div>
|
|
<div class="stat-details">
|
|
<h4 class="stat-value">{{ $waktuMakans->where('has_recommendation', true)->count() }}</h4>
|
|
<p class="stat-label">Waktu Makan Terhitung</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div class="stat-card bg-gradient-warning">
|
|
<div class="stat-icon">
|
|
<i class="fas fa-clock"></i>
|
|
</div>
|
|
<div class="stat-details">
|
|
<h4 class="stat-value">{{ $waktuMakans->where('has_recommendation', false)->count() }}</h4>
|
|
<p class="stat-label">Menunggu Perhitungan</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div class="stat-card bg-gradient-info">
|
|
<div class="stat-icon">
|
|
<i class="fas fa-calendar-alt"></i>
|
|
</div>
|
|
<div class="stat-details">
|
|
<h4 class="stat-value">{{ $waktuMakans->max('latest_calculation') ? \Carbon\Carbon::parse($waktuMakans->max('latest_calculation'))->format('d M Y') : 'Belum ada' }}</h4>
|
|
<p class="stat-label">Terakhir Update</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alerts -->
|
|
@if(session('error'))
|
|
<div class="admin-alert alert-danger animate-fade-in" role="alert">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-exclamation-circle me-2"></i>
|
|
<div>{{ session('error') }}</div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('success'))
|
|
<div class="admin-alert alert-success animate-fade-in" role="alert">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-check-circle me-2"></i>
|
|
<div>{{ session('success') }}</div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Waktu Makan Cards -->
|
|
<div class="row g-4" id="cardsContainer">
|
|
@foreach($waktuMakans as $waktuMakan)
|
|
@php
|
|
$isNewlyCalculated = $waktuMakan->latest_calculation &&
|
|
\Carbon\Carbon::parse($waktuMakan->latest_calculation)->isToday();
|
|
@endphp
|
|
<div class="col-12 col-md-6 col-lg-4 animate-fade-in" style="animation-delay: {{ $loop->iteration * 0.1 }}s">
|
|
<div class="recommendation-card {{ $isNewlyCalculated ? 'newly-calculated' : '' }}">
|
|
<div class="card-header {{ $isNewlyCalculated ? 'bg-gradient-success' : 'bg-gradient-primary' }}">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0 d-flex align-items-center">
|
|
<i class="fas fa-clock me-2"></i>
|
|
<span class="text-truncate">{{ $waktuMakan->nama }}</span>
|
|
</h5>
|
|
<div class="status-badge">
|
|
@if($waktuMakan->has_recommendation)
|
|
<span class="badge bg-light text-dark">
|
|
<i class="fas fa-check me-1"></i>Terhitung
|
|
</span>
|
|
@else
|
|
<span class="badge bg-warning">
|
|
<i class="fas fa-hourglass-half me-1"></i>Pending
|
|
</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="components-section mb-3">
|
|
<h6 class="section-title">
|
|
<i class="fas fa-utensils me-2"></i>Komponen Makanan
|
|
</h6>
|
|
<div class="components-container">
|
|
@foreach($waktuMakan->komponens as $komponen)
|
|
<span class="component-badge">
|
|
{{ $komponen->nama }}
|
|
</span>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
@if($waktuMakan->latest_calculation)
|
|
<div class="calculation-info">
|
|
<div class="info-item">
|
|
<i class="fas fa-calendar me-2"></i>
|
|
<span class="info-text">
|
|
Terakhir dihitung:
|
|
{{ \Carbon\Carbon::parse($waktuMakan->latest_calculation)->format('d M Y H:i') }}
|
|
@if($isNewlyCalculated)
|
|
<span class="badge bg-success ms-2">Baru</span>
|
|
@endif
|
|
</span>
|
|
</div>
|
|
@if($waktuMakan->latestConsistencyRatio)
|
|
<div class="info-item">
|
|
<i class="fas fa-calculator me-2"></i>
|
|
<span class="info-text">
|
|
CR: {{ number_format($waktuMakan->latestConsistencyRatio->nilai_cr, 3) }}
|
|
@if($waktuMakan->latestConsistencyRatio->nilai_cr < 0.1)
|
|
<span class="badge bg-success ms-1">Konsisten</span>
|
|
@else
|
|
<span class="badge bg-warning ms-1">Perlu Review</span>
|
|
@endif
|
|
</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="d-flex flex-wrap gap-2">
|
|
@if($waktuMakan->has_recommendation)
|
|
<a href="{{ route('rekomendasi.detail', $waktuMakan->id) }}"
|
|
class="action-btn btn-primary flex-grow-1">
|
|
<i class="fas fa-eye me-1"></i>Detail
|
|
</a>
|
|
@endif
|
|
<a href="{{ route('alternatif.pilih', ['waktu_makan' => $waktuMakan->id]) }}"
|
|
class="action-btn {{ $waktuMakan->has_recommendation ? 'btn-outline-primary' : 'btn-primary' }} flex-grow-1">
|
|
<i class="fas fa-calculator me-1"></i>
|
|
{{ $waktuMakan->has_recommendation ? 'Hitung Ulang' : 'Hitung' }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* Stats Cards */
|
|
.stats-overview {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.stat-card {
|
|
padding: 1.5rem;
|
|
border-radius: 1rem;
|
|
color: white;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
.stat-icon {
|
|
font-size: 2.5rem;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.stat-details {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 1.75rem;
|
|
font-weight: 600;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.stat-label {
|
|
margin-bottom: 0;
|
|
opacity: 0.9;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* Recommendation Cards */
|
|
.recommendation-card {
|
|
background: white;
|
|
border-radius: 1rem;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
transition: all 0.3s ease;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.recommendation-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.recommendation-card.newly-calculated {
|
|
border: 2px solid var(--bs-success);
|
|
}
|
|
|
|
.card-header {
|
|
padding: 1.25rem;
|
|
color: white;
|
|
}
|
|
|
|
.bg-gradient-primary {
|
|
background: linear-gradient(45deg, #2196f3, #64b5f6);
|
|
}
|
|
|
|
.bg-gradient-success {
|
|
background: linear-gradient(45deg, #4caf50, #81c784);
|
|
}
|
|
|
|
.bg-gradient-warning {
|
|
background: linear-gradient(45deg, #ff9800, #ffb74d);
|
|
}
|
|
|
|
.bg-gradient-info {
|
|
background: linear-gradient(45deg, #00bcd4, #4dd0e1);
|
|
}
|
|
|
|
.card-body {
|
|
padding: 1.25rem;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
color: #333;
|
|
}
|
|
|
|
.components-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.component-badge {
|
|
background: #e3f2fd;
|
|
color: #1976d2;
|
|
padding: 0.35rem 0.75rem;
|
|
border-radius: 2rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.calculation-info {
|
|
background: #f8f9fa;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 0.875rem;
|
|
color: #666;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.info-item:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.card-footer {
|
|
padding: 1.25rem;
|
|
background: #f8f9fa;
|
|
border-top: 1px solid #eee;
|
|
}
|
|
|
|
.action-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.5rem;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.action-btn.btn-primary {
|
|
background: #2196f3;
|
|
color: white;
|
|
}
|
|
|
|
.action-btn.btn-outline-primary {
|
|
border: 1px solid #2196f3;
|
|
color: #2196f3;
|
|
}
|
|
|
|
.action-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* Responsive Adjustments */
|
|
@media (max-width: 768px) {
|
|
.stat-card {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.stat-icon {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.recommendation-card {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.card-header h5 {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.component-badge {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.info-item {
|
|
font-size: 0.8125rem;
|
|
}
|
|
}
|
|
|
|
/* List View Styles */
|
|
.list-view .recommendation-card {
|
|
flex-direction: row;
|
|
align-items: center;
|
|
height: auto;
|
|
}
|
|
|
|
.list-view .card-header {
|
|
width: 25%;
|
|
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.list-view .card-body {
|
|
width: 50%;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.list-view .card-footer {
|
|
width: 25%;
|
|
border-top: none;
|
|
border-left: 1px solid #eee;
|
|
}
|
|
|
|
@media (max-width: 991.98px) {
|
|
.list-view .recommendation-card {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.list-view .card-header,
|
|
.list-view .card-body,
|
|
.list-view .card-footer {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Animations */
|
|
.animate-fade-in {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: all 0.5s ease;
|
|
}
|
|
|
|
.animate-fade-in.show {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
</style>
|
|
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Animation Observer
|
|
const animateElements = document.querySelectorAll('.animate-fade-in');
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('show');
|
|
}
|
|
});
|
|
}, {
|
|
threshold: 0.1
|
|
});
|
|
|
|
animateElements.forEach(element => observer.observe(element));
|
|
|
|
// Toggle View
|
|
const toggleViewBtn = document.getElementById('toggleView');
|
|
const cardsContainer = document.getElementById('cardsContainer');
|
|
let isListView = localStorage.getItem('recommendationViewMode') === 'list';
|
|
|
|
function updateViewMode() {
|
|
if (isListView) {
|
|
cardsContainer.classList.add('list-view');
|
|
toggleViewBtn.innerHTML = '<i class="fas fa-th-large me-1"></i>Grid View';
|
|
} else {
|
|
cardsContainer.classList.remove('list-view');
|
|
toggleViewBtn.innerHTML = '<i class="fas fa-list me-1"></i>List View';
|
|
}
|
|
}
|
|
|
|
updateViewMode();
|
|
|
|
toggleViewBtn.addEventListener('click', () => {
|
|
isListView = !isListView;
|
|
localStorage.setItem('recommendationViewMode', isListView ? 'list' : 'grid');
|
|
updateViewMode();
|
|
});
|
|
|
|
// Smooth Scroll
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
@endpush
|