1558 lines
81 KiB
PHP
1558 lines
81 KiB
PHP
@extends('layouts.admin')
|
||
|
||
@section('title', 'Hasil Rekomendasi SAW')
|
||
|
||
@section('content')
|
||
<div class="container-fluid px-3 px-md-4 py-3 py-md-4">
|
||
<!-- Enhanced Mobile-First Styles -->
|
||
<style>
|
||
.breadcrumb {
|
||
background: transparent;
|
||
border-radius: 10px;
|
||
padding: 0;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.breadcrumb-item a {
|
||
color: var(--primary-color);
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.breadcrumb-item a:hover {
|
||
color: var(--secondary-color);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.breadcrumb-item.active {
|
||
color: var(--text-secondary);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.detail-header {
|
||
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
|
||
color: white;
|
||
padding: 1.5rem;
|
||
border-radius: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
box-shadow: 0 10px 25px rgba(var(--primary-color-rgb), 0.25);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.detail-header::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: radial-gradient(circle at top right, rgba(255,255,255,0.1) 0%, transparent 50%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.detail-header h2 {
|
||
font-size: 1.5rem;
|
||
font-weight: 800;
|
||
margin-bottom: 0.75rem;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.detail-card {
|
||
border: none;
|
||
border-radius: 1rem;
|
||
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
|
||
overflow: hidden;
|
||
margin-bottom: 1.5rem;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.detail-card-header {
|
||
color: var(--primary-color);
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
padding: 1rem 1.25rem;
|
||
border-bottom: 2px solid rgba(var(--primary-color-rgb), 0.1);
|
||
background: rgba(248, 249, 250, 0.8);
|
||
backdrop-filter: blur(5px);
|
||
}
|
||
|
||
.btn-back {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
color: var(--primary-color);
|
||
font-weight: 700;
|
||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||
padding: 0.75rem 1.5rem;
|
||
border-radius: 50px;
|
||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
backdrop-filter: blur(10px);
|
||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.btn-back:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||
background: white;
|
||
color: var(--secondary-color);
|
||
border-color: white;
|
||
}
|
||
|
||
.glass-card {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
border-radius: 1rem;
|
||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.ranking-card {
|
||
transition: all 0.3s ease;
|
||
border-radius: 1rem;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.ranking-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
||
}
|
||
|
||
.ranking-badge {
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 50px;
|
||
font-weight: 700;
|
||
font-size: 0.9rem;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.ranking-badge.rank-1 {
|
||
background: linear-gradient(135deg, #ffd700, #ffed4e);
|
||
color: #8b5cf6;
|
||
box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
|
||
}
|
||
|
||
.ranking-badge.rank-2 {
|
||
background: linear-gradient(135deg, #c0c0c0, #e5e5e5);
|
||
color: #374151;
|
||
box-shadow: 0 4px 15px rgba(192, 192, 192, 0.3);
|
||
}
|
||
|
||
.ranking-badge.rank-3 {
|
||
background: linear-gradient(135deg, #cd7f32, #d4943d);
|
||
color: white;
|
||
box-shadow: 0 4px 15px rgba(205, 127, 50, 0.3);
|
||
}
|
||
|
||
/* Mobile optimizations */
|
||
@media (max-width: 768px) {
|
||
.detail-header {
|
||
padding: 1.25rem;
|
||
margin-bottom: 1.25rem;
|
||
border-radius: 0.75rem;
|
||
}
|
||
|
||
.detail-header h2 {
|
||
font-size: 1.4rem;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.btn-back {
|
||
padding: 0.6rem 1.2rem;
|
||
font-size: 0.9rem;
|
||
border-radius: 25px;
|
||
}
|
||
|
||
.glass-card {
|
||
margin-left: -15px;
|
||
margin-right: -15px;
|
||
border-radius: 0.75rem;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 576px) {
|
||
.detail-header h2 {
|
||
font-size: 1.25rem;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<!-- Enhanced Breadcrumb -->
|
||
<nav aria-label="breadcrumb" class="mb-3 mb-md-4">
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item">
|
||
<a href="{{ route('dashboard') }}" class="d-flex align-items-center">
|
||
<i class="bi bi-house-door me-1"></i>
|
||
<span class="d-none d-sm-inline">Dashboard</span>
|
||
<span class="d-sm-none">Home</span>
|
||
</a>
|
||
</li>
|
||
<li class="breadcrumb-item">
|
||
<a href="{{ route('saw.index') }}" class="d-flex align-items-center">
|
||
<i class="bi bi-calculator me-1"></i>
|
||
<span class="d-none d-sm-inline">Metode SAW</span>
|
||
<span class="d-sm-none">SAW</span>
|
||
</a>
|
||
</li>
|
||
<li class="breadcrumb-item active" aria-current="page">
|
||
<i class="bi bi-trophy me-1"></i>Hasil
|
||
</li>
|
||
</ol>
|
||
</nav>
|
||
|
||
<!-- Header -->
|
||
<div class="detail-header">
|
||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center gap-3">
|
||
<div>
|
||
<h2 class="mb-1">
|
||
<i class="bi bi-trophy-fill me-2 d-md-none"></i>
|
||
Hasil SAW {{ ucfirst($tipe) }}
|
||
</h2>
|
||
<p class="mb-0 opacity-90" style="font-size: 1rem; font-weight: 500;">
|
||
<i class="bi bi-calculator me-1"></i>
|
||
Simple Additive Weighting Method
|
||
</p>
|
||
<!-- INFO REFERENCE POINT -->
|
||
@if($tipe == 'kontrakan')
|
||
<span class="badge bg-light text-dark" style="color: white !important;">
|
||
<i class="bi bi-geo-alt-fill me-1"></i>Jarak dari Kampus Polije
|
||
</span>
|
||
@else
|
||
<span class="badge bg-light text-dark" style="color: white !important;">
|
||
<i class="bi bi-geo-alt-fill me-1"></i>Jarak dari Lokasi Anda
|
||
</span>
|
||
@endif
|
||
</div>
|
||
<a href="{{ route('saw.index') }}" class="btn btn-back">
|
||
<i class="bi bi-arrow-left me-2"></i>Kembali
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Info Alert -->
|
||
<div class="alert border-0 mb-4 shadow-sm" style="background: linear-gradient(135deg, rgba(var(--info-color-rgb), 0.1) 0%, rgba(var(--primary-color-rgb), 0.1) 100%); border-left: 4px solid var(--info-color); border-radius: 1rem;">
|
||
<div class="d-flex align-items-start">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-3 rounded-circle" style="background: rgba(var(--info-color-rgb), 0.15); color: var(--info-color); min-width: 40px; height: 40px;">
|
||
<i class="bi bi-info-circle-fill" style="font-size: 1.2rem;"></i>
|
||
</div>
|
||
<div class="flex-grow-1">
|
||
<h6 class="fw-bold mb-2" style="color: var(--info-color);">
|
||
<i class="bi bi-geo-alt me-1"></i>
|
||
Informasi Perhitungan Jarak
|
||
</h6>
|
||
<p class="mb-2" style="color: var(--text-secondary); line-height: 1.6;">
|
||
@if($tipe == 'kontrakan')
|
||
Jarak <strong>kontrakan</strong> dihitung secara otomatis dari <strong style="color: var(--primary-color);">Kampus Politeknik Negeri Jember</strong> menggunakan koordinat GPS.
|
||
@else
|
||
Jarak <strong>laundry</strong> dihitung secara otomatis dari <strong style="color: var(--primary-color);">lokasi Anda saat ini</strong> menggunakan koordinat GPS.
|
||
@endif
|
||
</p>
|
||
<small style="color: var(--text-muted);">
|
||
<i class="bi bi-lightbulb me-1"></i>
|
||
<strong>Tips:</strong> Sistem menampilkan {{ $tipe }} dengan ranking berdasarkan nilai SAW tertinggi.
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Peta Lokasi Card -->
|
||
<div class="glass-card mb-4">
|
||
<div class="card-header border-0 py-3 py-md-4" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-3">
|
||
<div>
|
||
<h6 class="mb-1 fw-bold" style="color: var(--text-primary);">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color);">
|
||
<i class="bi bi-map-fill"></i>
|
||
</div>
|
||
<span class="d-none d-sm-inline">Peta Lokasi Rekomendasi</span>
|
||
<span class="d-sm-none">Peta Lokasi</span>
|
||
</div>
|
||
</h6>
|
||
<small style="color: var(--text-muted);">Klik marker untuk detail informasi</small>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<button class="btn btn-sm rounded-pill" onclick="resetMapView()" style="background: rgba(var(--primary-color-rgb), 0.1); color: var(--primary-color); border: 1px solid rgba(var(--primary-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-arrow-clockwise me-1"></i>
|
||
<span class="d-none d-sm-inline">Reset View</span>
|
||
<span class="d-sm-none">Reset</span>
|
||
</button>
|
||
<button class="btn btn-sm rounded-pill d-none d-md-block" onclick="toggleMapFullscreen()" style="background: rgba(var(--success-color-rgb), 0.1); color: var(--success-color); border: 1px solid rgba(var(--success-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-arrows-fullscreen me-1"></i>Fullscreen
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div id="map" class="rounded-bottom" style="height: 350px; width: 100%; position: relative;"></div>
|
||
</div>
|
||
<div class="card-footer border-0 py-3 py-md-4" style="background: rgba(248, 249, 250, 0.8); backdrop-filter: blur(5px);">
|
||
<div class="row g-3">
|
||
<!-- Enhanced Legend -->
|
||
<div class="col-12 col-lg-8">
|
||
<h6 class="fw-bold mb-3 fs-6">
|
||
<i class="bi bi-palette text-primary me-2"></i>Legenda Peta
|
||
</h6>
|
||
<div class="d-flex flex-wrap gap-3 small">
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(255, 193, 7, 0.15); border: 1px solid rgba(255, 193, 7, 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: linear-gradient(135deg, #ffd700, #ffed4e); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(255, 215, 0, 0.3);"></div>
|
||
<span class="fw-bold" style="color: #8b5cf6;">🏆 Rank 1</span>
|
||
</div>
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(108, 117, 125, 0.15); border: 1px solid rgba(108, 117, 125, 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: linear-gradient(135deg, #c0c0c0, #e5e5e5); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(192, 192, 192, 0.3);"></div>
|
||
<span class="fw-bold" style="color: #374151;">🥈 Rank 2</span>
|
||
</div>
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(205, 127, 50, 0.15); border: 1px solid rgba(205, 127, 50, 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: linear-gradient(135deg, #cd7f32, #d4943d); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(205, 127, 50, 0.3);"></div>
|
||
<span class="fw-bold text-white" style="color: white;">🥉 Rank 3</span>
|
||
</div>
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(var(--primary-color-rgb), 0.15); border: 1px solid rgba(var(--primary-color-rgb), 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: var(--primary-color); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(var(--primary-color-rgb), 0.3);"></div>
|
||
<span class="fw-bold" style="color: var(--primary-color);">Lainnya</span>
|
||
</div>
|
||
@if($tipe == 'kontrakan')
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(var(--info-color-rgb), 0.15); border: 1px solid rgba(var(--info-color-rgb), 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: var(--info-color); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(var(--info-color-rgb), 0.3);"></div>
|
||
<span class="fw-bold" style="color: var(--info-color);"><i class="bi bi-building me-1"></i>Polije</span>
|
||
</div>
|
||
</div>
|
||
@else
|
||
<div class="d-flex align-items-center px-3 py-2 rounded-pill" style="background: rgba(var(--success-color-rgb), 0.15); border: 1px solid rgba(var(--success-color-rgb), 0.3);">
|
||
<div class="rounded-circle me-2" style="width: 16px; height: 16px; background: var(--success-color); border: 2px solid #fff; box-shadow: 0 2px 6px rgba(var(--success-color-rgb), 0.3);"></div>
|
||
<span class="fw-bold" style="color: var(--success-color);"><i class="bi bi-geo me-1"></i>Lokasi Anda</span>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Tips -->
|
||
<div class="col-12 col-lg-4">
|
||
<div class="p-3 rounded-3" style="background: rgba(var(--info-color-rgb), 0.1); border: 1px solid rgba(var(--info-color-rgb), 0.2);">
|
||
<h6 class="fw-bold mb-2" style="color: var(--info-color); font-size: 0.9rem;">
|
||
<i class="bi bi-lightbulb me-1"></i>Tips Navigasi
|
||
</h6>
|
||
<div class="d-flex flex-column gap-1">
|
||
<small style="color: var(--text-muted); font-size: 0.8rem;">
|
||
<i class="bi bi-cursor me-1" style="color: var(--info-color);"></i>Klik marker untuk detail lengkap
|
||
</small>
|
||
<small style="color: var(--text-muted); font-size: 0.8rem;">
|
||
<i class="bi bi-mouse me-1" style="color: var(--info-color);"></i>Scroll/pinch untuk zoom
|
||
</small>
|
||
<small style="color: var(--text-muted); font-size: 0.8rem;">
|
||
<i class="bi bi-arrows-move me-1" style="color: var(--info-color);"></i>Drag untuk menggeser peta
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Kriteria & Bobot Simulasi Card -->
|
||
<div class="glass-card mb-4">
|
||
<div class="card-header border-0 py-3 py-md-4" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex align-items-center justify-content-between">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--warning-color-rgb), 0.15); color: var(--warning-color);">
|
||
<i class="bi bi-star-fill"></i>
|
||
</div>
|
||
<h6 class="mb-0 fw-bold" style="color: var(--text-primary);">
|
||
<span class="d-none d-sm-inline">Kriteria dan Bobot Simulasi yang Digunakan</span>
|
||
<span class="d-sm-none">Kriteria & Bobot Simulasi</span>
|
||
</h6>
|
||
</div>
|
||
<span class="badge rounded-pill px-3 py-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700;">
|
||
{{ count($kriteria) }} Kriteria
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-3 p-md-4">
|
||
<div class="row g-3">
|
||
@foreach($kriteria as $k)
|
||
<div class="col-6 col-lg-3">
|
||
<div class="card border-0 h-100" style="background: rgba(248, 249, 250, 0.8); backdrop-filter: blur(5px); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(0,0,0,0.1)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow=''">
|
||
<div class="card-body text-center p-3">
|
||
<div class="mb-3">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center mx-auto mb-2 rounded-circle" style="background: rgba(var(--primary-color-rgb), 0.1); color: var(--primary-color);">
|
||
<i class="bi bi-star"></i>
|
||
</div>
|
||
<h6 class="fw-bold mb-0 small" style="color: var(--text-primary); line-height: 1.3;">{{ $k->nama_kriteria }}</h6>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<span class="badge px-3 py-2 rounded-3" style="background: rgba(var(--info-color-rgb), 0.15); color: var(--info-color); font-weight: 700; font-size: 0.9rem;">{{ $k->bobot }}</span>
|
||
<small class="d-block mt-1 text-muted">({{ number_format($k->bobot * 100, 1) }}%)</small>
|
||
</div>
|
||
|
||
@if(strtolower($k->tipe) == 'benefit')
|
||
<span class="badge px-2 py-1 rounded-3" style="background: rgba(var(--success-color-rgb), 0.15); color: var(--success-color); font-size: 0.75rem; font-weight: 600;">
|
||
<i class="bi bi-arrow-up-circle me-1"></i>Benefit
|
||
</span>
|
||
@else
|
||
<span class="badge px-2 py-1 rounded-3" style="background: rgba(var(--danger-color-rgb), 0.15); color: var(--danger-color); font-size: 0.75rem; font-weight: 600;">
|
||
<i class="bi bi-arrow-down-circle me-1"></i>Cost
|
||
</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
|
||
<div class="text-center mt-4">
|
||
<div class="d-inline-flex align-items-center px-4 py-3 rounded-pill" style="background: rgba(var(--primary-color-rgb), 0.1); border: 2px solid rgba(var(--primary-color-rgb), 0.2);">
|
||
<i class="bi bi-calculator me-2" style="color: var(--primary-color); font-size: 1.2rem;"></i>
|
||
<span class="fw-bold" style="color: var(--primary-color);">Total Bobot: {{ $kriteria->sum('bobot') }}</span>
|
||
<span class="ms-2 badge rounded-pill" style="background: var(--primary-color); color: white; font-size: 0.7rem;">{{ $kriteria->sum('bobot') == 1 ? 'VALID' : 'CHECK' }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Visualisasi Chart -->
|
||
<div class="row g-3 g-md-4 mb-4">
|
||
<!-- Enhanced Bar Chart -->
|
||
<div class="col-12 col-lg-8">
|
||
<div class="glass-card">
|
||
<div class="card-header border-0 py-3 py-md-4" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center gap-3">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color);">
|
||
<i class="bi bi-bar-chart-fill"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="mb-1 fw-bold" style="color: var(--text-primary);">
|
||
<span class="d-none d-sm-inline">Perbandingan Nilai SAW</span>
|
||
<span class="d-sm-none">Chart SAW</span>
|
||
</h6>
|
||
<small style="color: var(--text-muted);">Ranking berdasarkan nilai tertinggi</small>
|
||
</div>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<span class="badge rounded-pill px-3 py-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 600;">
|
||
<i class="bi bi-graph-up me-1"></i>Live Chart
|
||
</span>
|
||
<button class="btn btn-sm rounded-pill d-none d-md-block" onclick="downloadChart()" style="background: rgba(var(--success-color-rgb), 0.1); color: var(--success-color); border: 1px solid rgba(var(--success-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-download me-1"></i>Download
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-3 p-md-4">
|
||
<div class="chart-container" style="position: relative; height: 350px;">
|
||
<canvas id="barChartSAW"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="card-footer border-0 py-3" style="background: rgba(248, 249, 250, 0.8); backdrop-filter: blur(5px);">
|
||
<div class="d-flex align-items-center">
|
||
<i class="bi bi-info-circle me-2" style="color: var(--info-color);"></i>
|
||
<small style="color: var(--text-muted); line-height: 1.5;">
|
||
Grafik menunjukkan perbandingan nilai SAW. <strong style="color: var(--primary-color);">Semakin tinggi nilai, semakin direkomendasikan</strong> berdasarkan kriteria yang dipilih.
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Radar Chart -->
|
||
<div class="col-12 col-lg-4">
|
||
<div class="glass-card">
|
||
<div class="card-header border-0 py-3 py-md-4" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--success-color-rgb), 0.15); color: var(--success-color);">
|
||
<i class="bi bi-diagram-3-fill"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="mb-1 fw-bold" style="color: var(--text-primary);">
|
||
<span class="d-none d-sm-inline">Analisis Top 3</span>
|
||
<span class="d-sm-none">Top 3</span>
|
||
</h6>
|
||
<small style="color: var(--text-muted);">Perbandingan multi-kriteria</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-3 p-md-4">
|
||
<div class="chart-container" style="position: relative; height: 300px;">
|
||
<canvas id="radarChartTop3"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="card-footer bg-white border-0 py-2 py-md-3">
|
||
<small class="text-muted" style="font-size: 0.8rem;">
|
||
<i class="bi bi-info-circle me-1"></i>
|
||
Perbandingan multi-kriteria
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Progress Bars per Kriteria -->
|
||
<div class="card border-0 shadow-sm mb-3 mb-md-4">
|
||
<div class="card-header bg-white border-0 py-2 py-md-3">
|
||
<h6 class="fw-bold mb-0 fs-6">
|
||
<i class="bi bi-speedometer2 text-warning me-2"></i>Detail Nilai per Kriteria (Top 5)
|
||
</h6>
|
||
</div>
|
||
<div class="card-body p-2 p-md-3">
|
||
@foreach($kriteria as $k)
|
||
<div class="mb-3 mb-md-4">
|
||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-2 gap-2">
|
||
<h6 class="fw-semibold mb-0 small">
|
||
{{ $k->nama_kriteria }}
|
||
<span class="badge bg-{{ strtolower($k->tipe) == 'benefit' ? 'success' : 'danger' }} ms-2">
|
||
{{ strtolower($k->tipe) == 'benefit' ? '↑ Benefit' : '↓ Cost' }}
|
||
</span>
|
||
</h6>
|
||
<span class="badge bg-info small">Bobot: {{ $k->bobot }}</span>
|
||
</div>
|
||
|
||
@foreach(array_slice($hasil, 0, 5) as $index => $item)
|
||
@php
|
||
$nilaiKriteria = $item['normalisasi'][$k->nama_kriteria] ?? null;
|
||
$percentage = $nilaiKriteria ? ($nilaiKriteria['normalisasi'] * 100) : 0;
|
||
$barColor = $index == 0 ? 'warning' : ($index == 1 ? 'secondary' : ($index == 2 ? 'danger' : 'primary'));
|
||
@endphp
|
||
|
||
@if($nilaiKriteria)
|
||
<div class="mb-2">
|
||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||
<small class="fw-semibold" style="font-size: 0.85rem;">
|
||
@if($index == 0)
|
||
🏆
|
||
@elseif($index == 1)
|
||
🥈
|
||
@elseif($index == 2)
|
||
🥉
|
||
@else
|
||
{{ $index + 1 }}.
|
||
@endif
|
||
{{ Str::limit($item['nama'], 30) }}
|
||
</small>
|
||
<small class="text-muted" style="font-size: 0.75rem;">
|
||
<strong>{{ $nilaiKriteria['asli'] }}</strong>
|
||
<span class="text-primary ms-1">({{ number_format($nilaiKriteria['normalisasi'], 4) }})</span>
|
||
</small>
|
||
</div>
|
||
<div class="progress" style="height: 6px;">
|
||
<div
|
||
class="progress-bar bg-{{ $barColor }}"
|
||
role="progressbar"
|
||
style="width: {{ $percentage }}%;"
|
||
aria-valuenow="{{ $percentage }}"
|
||
aria-valuemin="0"
|
||
aria-valuemax="100"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
@endforeach
|
||
|
||
@if($loop->iteration < count($kriteria))
|
||
<hr class="my-2 my-md-3">
|
||
@endif
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 🆕 Export Results Buttons -->
|
||
<div class="row g-2 mb-4">
|
||
<div class="col-12">
|
||
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between">
|
||
<span class="badge bg-info bg-opacity-10 text-info px-3 py-2">
|
||
<i class="bi bi-check-circle me-1"></i>
|
||
Analisis SAW Selesai - {{ count($hasil) }} hasil
|
||
</span>
|
||
<div class="btn-group" role="group">
|
||
<button type="button" class="btn btn-sm btn-outline-success" id="btnExcelSAW" title="Export ke CSV">
|
||
<i class="bi bi-file-earmark-spreadsheet me-1"></i>CSV
|
||
</button>
|
||
<button type="button" class="btn btn-sm btn-outline-danger" id="btnPdfSAW" title="Export ke PDF">
|
||
<i class="bi bi-file-earmark-pdf me-1"></i>PDF
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Hidden Forms untuk Export -->
|
||
<form id="formExcelSAW" method="POST" action="{{ route('export.saw.excel') }}" style="display: none;">
|
||
@csrf
|
||
<input type="hidden" name="hasil_json">
|
||
<input type="hidden" name="tipe" value="{{ $tipe }}">
|
||
<input type="hidden" name="jenis_layanan" value="{{ $jenisLayanan ?? '' }}">
|
||
</form>
|
||
|
||
<form id="formPdfSAW" method="POST" action="{{ route('export.saw.pdf') }}" style="display: none;">
|
||
@csrf
|
||
<input type="hidden" name="hasil_json">
|
||
<input type="hidden" name="tipe" value="{{ $tipe }}">
|
||
<input type="hidden" name="jenis_layanan" value="{{ $jenisLayanan ?? '' }}">
|
||
</form>
|
||
|
||
<!-- Enhanced Mobile Card View -->
|
||
<div class="d-lg-none mb-4">
|
||
<div class="glass-card">
|
||
<div class="card-header border-0 py-3" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex align-items-center justify-content-between">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--warning-color-rgb), 0.15); color: var(--warning-color);">
|
||
<i class="bi bi-trophy-fill"></i>
|
||
</div>
|
||
<h6 class="mb-0 fw-bold" style="color: var(--text-primary);">
|
||
<span class="d-none d-sm-inline">Peringkat Rekomendasi</span>
|
||
<span class="d-sm-none">Ranking</span>
|
||
</h6>
|
||
</div>
|
||
<span class="badge rounded-pill px-3 py-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700;">
|
||
{{ count($hasil) }} {{ ucfirst($tipe) }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
@foreach($hasil as $index => $item)
|
||
<div class="ranking-card p-3 border-bottom {{ $index == 0 ? 'border-start border-5 border-warning' : '' }}"
|
||
id="mobile-row-{{ $index }}"
|
||
style="{{ $index == 0 ? 'background: linear-gradient(135deg, rgba(255, 193, 7, 0.1) 0%, rgba(255, 215, 0, 0.1) 100%);' : '' }} transition: all 0.3s ease;"
|
||
ontouchstart="this.style.transform='scale(0.98)'"
|
||
ontouchend="this.style.transform='scale(1)'">
|
||
|
||
<!-- Header Row -->
|
||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||
<div class="flex-grow-1">
|
||
<!-- Ranking Badge -->
|
||
@if($index == 0)
|
||
<div class="ranking-badge rank-1 mb-2">
|
||
<i class="bi bi-trophy-fill"></i>
|
||
<span>#1 - Terbaik</span>
|
||
</div>
|
||
@elseif($index == 1)
|
||
<div class="ranking-badge rank-2 mb-2">
|
||
<i class="bi bi-award-fill"></i>
|
||
<span>#2 - Baik</span>
|
||
</div>
|
||
@elseif($index == 2)
|
||
<div class="ranking-badge rank-3 mb-2">
|
||
<i class="bi bi-award-fill"></i>
|
||
<span>#3 - Cukup</span>
|
||
</div>
|
||
@else
|
||
<div class="badge rounded-pill px-3 py-2 mb-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700;">
|
||
<i class="bi bi-hash me-1"></i>{{ $item['ranking'] }}
|
||
</div>
|
||
@endif
|
||
|
||
<!-- Name & Location -->
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); line-height: 1.3;">{{ $item['nama'] }}</h6>
|
||
<p class="small mb-0" style="color: var(--text-muted); line-height: 1.4;">
|
||
<i class="bi bi-geo-alt-fill me-1" style="color: var(--danger-color);"></i>
|
||
{{ Str::limit($item['alamat'], 60) }}
|
||
</p>
|
||
</div>
|
||
|
||
<!-- SAW Score -->
|
||
<div class="text-end">
|
||
<div class="badge px-3 py-2 rounded-3" style="background: rgba(var(--{{ $index == 0 ? 'success' : ($index == 1 ? 'info' : ($index == 2 ? 'warning' : 'primary')) }}-color-rgb), 0.15); color: var(--{{ $index == 0 ? 'success' : ($index == 1 ? 'info' : ($index == 2 ? 'warning' : 'primary')) }}-color); font-weight: 700; font-size: 1rem;">
|
||
{{ number_format($item['nilai'], 4) }}
|
||
</div>
|
||
<small class="d-block mt-1" style="color: var(--text-muted);">Skor SAW</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Achievement Badge -->
|
||
@if($index == 0)
|
||
<div class="alert border-0 mb-3 py-2" style="background: rgba(var(--success-color-rgb), 0.1); border-left: 4px solid var(--success-color) !important;">
|
||
<small class="fw-bold" style="color: var(--success-color);">✨ Rekomendasi Terbaik Berdasarkan Bobot Simulasi</small>
|
||
</div>
|
||
@elseif($index == 1)
|
||
<div class="alert border-0 mb-3 py-2" style="background: rgba(var(--info-color-rgb), 0.1); border-left: 4px solid var(--info-color) !important;">
|
||
<small class="fw-bold" style="color: var(--info-color);">🥈 Pilihan Alternatif Terbaik Kedua</small>
|
||
</div>
|
||
@elseif($index == 2)
|
||
<div class="alert border-0 mb-3 py-2" style="background: rgba(var(--warning-color-rgb), 0.1); border-left: 4px solid var(--warning-color) !important;">
|
||
<small class="fw-bold" style="color: var(--warning-color);">🥉 Pilihan Ketiga yang Layak Dipertimbangkan</small>
|
||
</div>
|
||
@endif
|
||
|
||
<!-- Action Buttons -->
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
<button class="btn btn-sm rounded-pill flex-fill"
|
||
onclick="focusOnMarker({{ $index }})"
|
||
style="background: rgba(var(--primary-color-rgb), 0.1); color: var(--primary-color); border: 1px solid rgba(var(--primary-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-geo-alt-fill me-1"></i>
|
||
<span class="d-none d-sm-inline">Lihat di Peta</span>
|
||
<span class="d-sm-none">Peta</span>
|
||
</button>
|
||
|
||
@if(isset($item['latitude']) && isset($item['longitude']) && $item['latitude'] && $item['longitude'])
|
||
<a href="https://www.google.com/maps/dir/?api=1&destination={{ $item['latitude'] }},{{ $item['longitude'] }}"
|
||
target="_blank"
|
||
class="btn btn-sm rounded-pill flex-fill"
|
||
style="background: rgba(var(--danger-color-rgb), 0.1); color: var(--danger-color); border: 1px solid rgba(var(--danger-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-google me-1"></i>
|
||
<span class="d-none d-sm-inline">Google Maps</span>
|
||
<span class="d-sm-none">Maps</span>
|
||
</a>
|
||
@endif
|
||
|
||
@if(isset($item['no_whatsapp']) && !empty($item['no_whatsapp']))
|
||
<a href="https://wa.me/{{ preg_replace('/[^0-9]/', '', $item['no_whatsapp']) }}"
|
||
target="_blank"
|
||
class="btn btn-sm rounded-pill flex-fill"
|
||
style="background: rgba(var(--success-color-rgb), 0.1); color: var(--success-color); border: 1px solid rgba(var(--success-color-rgb), 0.3); font-weight: 600;">
|
||
<i class="bi bi-whatsapp me-1"></i>
|
||
<span class="d-none d-sm-inline">WhatsApp</span>
|
||
<span class="d-sm-none">Chat</span>
|
||
</a>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Desktop Table View -->
|
||
<div class="glass-card mb-4 d-none d-lg-block">
|
||
<div class="card-header border-0 py-3 py-md-4" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center gap-3">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--warning-color-rgb), 0.15); color: var(--warning-color);">
|
||
<i class="bi bi-trophy-fill"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="mb-1 fw-bold" style="color: var(--text-primary);">Peringkat Rekomendasi</h6>
|
||
<small style="color: var(--text-muted);">Data lengkap perhitungan SAW</small>
|
||
</div>
|
||
</div>
|
||
<div class="d-flex gap-2 align-items-center">
|
||
<span class="badge rounded-pill px-3 py-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700;">
|
||
{{ count($hasil) }} Hasil
|
||
</span>
|
||
<button class="btn btn-sm btn-outline-primary rounded-pill px-3 py-1" onclick="window.print()" title="Print Hasil">
|
||
<i class="bi bi-printer me-1"></i>Print
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div class="table-responsive" style="max-height: 600px; overflow-y: auto;">
|
||
<style>
|
||
.table-responsive::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
.table-responsive::-webkit-scrollbar-track {
|
||
background: rgba(0,0,0,0.05);
|
||
border-radius: 3px;
|
||
}
|
||
.table-responsive::-webkit-scrollbar-thumb {
|
||
background: rgba(var(--primary-color-rgb), 0.3);
|
||
border-radius: 3px;
|
||
}
|
||
.table-responsive::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(var(--primary-color-rgb), 0.5);
|
||
}
|
||
.table tbody tr {
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
.table tbody tr:hover {
|
||
background: rgba(var(--primary-color-rgb), 0.05) !important;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||
}
|
||
.table tbody tr.table-warning:hover {
|
||
background: rgba(var(--warning-color-rgb), 0.2) !important;
|
||
box-shadow: 0 4px 20px rgba(255, 193, 7, 0.3);
|
||
}
|
||
.table tbody tr.table-warning {
|
||
background: rgba(var(--warning-color-rgb), 0.1) !important;
|
||
border-left: 3px solid var(--warning-color);
|
||
}
|
||
</style>
|
||
<table class="table table-hover align-middle mb-0">
|
||
<thead style="background: rgba(248, 249, 250, 0.8); backdrop-filter: blur(5px);">
|
||
<tr>
|
||
<th class="border-0 px-4 py-3" style="border-radius: 0;">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px;">🏆 Peringkat</small>
|
||
</th>
|
||
<th class="border-0 py-3">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px;">Nama {{ ucfirst($tipe) }}</small>
|
||
</th>
|
||
<th class="border-0 py-3">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px;">🗺 Alamat</small>
|
||
</th>
|
||
@foreach($kriteria as $k)
|
||
<th class="border-0 py-3 text-center">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px; font-size: 0.75rem;">{{ Str::limit($k->nama_kriteria, 15) }}</small>
|
||
</th>
|
||
@endforeach
|
||
<th class="border-0 py-3 text-center">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px;">🏆 Nilai SAW</small>
|
||
</th>
|
||
<th class="border-0 py-3 text-center">
|
||
<small class="text-uppercase fw-bold" style="color: var(--text-muted); letter-spacing: 0.5px;">⚙️ Aksi</small>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@foreach($hasil as $index => $item)
|
||
<tr class="{{ $index == 0 ? 'table-warning table-warning-light' : '' }}" id="row-{{ $index }}" style="transition: all 0.2s ease;">
|
||
<td class="px-4">
|
||
@if($index == 0)
|
||
<div class="ranking-badge rank-1">
|
||
<i class="bi bi-trophy-fill"></i>
|
||
<span>#{{ $item['ranking'] }}</span>
|
||
</div>
|
||
@elseif($index == 1)
|
||
<div class="ranking-badge rank-2">
|
||
<i class="bi bi-award-fill"></i>
|
||
<span>#{{ $item['ranking'] }}</span>
|
||
</div>
|
||
@elseif($index == 2)
|
||
<div class="ranking-badge rank-3">
|
||
<i class="bi bi-award-fill"></i>
|
||
<span>#{{ $item['ranking'] }}</span>
|
||
</div>
|
||
@else
|
||
<div class="badge rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px; background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700;">
|
||
{{ $item['ranking'] }}
|
||
</div>
|
||
@endif
|
||
</td>
|
||
<td>
|
||
<div class="d-flex align-items-center">
|
||
@if(isset($item['foto']) && $item['foto'])
|
||
<img src="{{ asset('uploads/' . $item['foto']) }}"
|
||
alt="{{ $item['nama'] }}"
|
||
class="rounded-circle object-cover me-2"
|
||
style="width: 40px; height: 40px; border: 2px solid rgba(var(--primary-color-rgb), 0.2); flex-shrink: 0;">
|
||
@else
|
||
<div class="rounded-circle bg-gradient-primary d-flex align-items-center justify-content-center text-white fw-bold me-2" style="width: 40px; height: 40px; font-size: 0.8rem; flex-shrink: 0;">
|
||
{{ strtoupper(substr($item['nama'], 0, 2)) }}
|
||
</div>
|
||
@endif
|
||
<div style="min-width: 0;">
|
||
<h6 class="mb-1 fw-bold text-truncate" style="color: var(--text-primary); font-size: 0.95rem;">{{ Str::limit($item['nama'], 25) }}</h6>
|
||
@if($index == 0)
|
||
<small class="text-success fw-bold d-flex align-items-center" style="font-size: 0.75rem;">
|
||
<i class="bi bi-trophy-fill me-1"></i>Terbaik
|
||
</small>
|
||
@elseif($index == 1)
|
||
<small class="text-secondary fw-bold d-flex align-items-center" style="font-size: 0.75rem;">
|
||
<i class="bi bi-award-fill me-1"></i>Runner-up
|
||
</small>
|
||
@elseif($index == 2)
|
||
<small class="text-danger fw-bold d-flex align-items-center" style="font-size: 0.75rem;">
|
||
<i class="bi bi-award-fill me-1"></i>Ketiga
|
||
</small>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<div class="d-flex align-items-center text-muted" style="font-size: 0.9rem;">
|
||
<i class="bi bi-geo-alt me-2" style="color: var(--danger-color); flex-shrink: 0;"></i>
|
||
<span class="text-truncate">{{ Str::limit($item['alamat'], 35) }}</span>
|
||
</div>
|
||
</td>
|
||
@foreach($kriteria as $k)
|
||
<td class="text-center">
|
||
@if(isset($item['normalisasi'][$k->nama_kriteria]))
|
||
<small class="text-muted d-block" style="font-size: 0.75rem;">
|
||
{{ $item['normalisasi'][$k->nama_kriteria]['asli'] }}
|
||
</small>
|
||
<small class="badge bg-info bg-opacity-25 text-dark">
|
||
{{ $item['normalisasi'][$k->nama_kriteria]['normalisasi'] }}
|
||
</small>
|
||
@else
|
||
<small class="text-muted">-</small>
|
||
@endif
|
||
</td>
|
||
@endforeach
|
||
<td class="text-center">
|
||
<div class="d-flex flex-column align-items-center">
|
||
@if($index == 0)
|
||
<div class="badge rounded-pill px-3 py-2 mb-1" style="background: linear-gradient(135deg, #ffd700, #ffed4e); color: #8B5A00; font-weight: 700; font-size: 0.9rem; box-shadow: 0 4px 12px rgba(255, 215, 0, 0.3);">
|
||
🏆 {{ number_format($item['nilai'], 4) }}
|
||
</div>
|
||
<small class="text-success fw-bold" style="font-size: 0.7rem;">TERBAIK</small>
|
||
@elseif($index == 1)
|
||
<div class="badge rounded-pill px-3 py-2 mb-1" style="background: linear-gradient(135deg, #c0c0c0, #e5e5e5); color: #666; font-weight: 700; font-size: 0.9rem; box-shadow: 0 4px 12px rgba(192, 192, 192, 0.3);">
|
||
🥈 {{ number_format($item['nilai'], 4) }}
|
||
</div>
|
||
<small class="text-secondary fw-bold" style="font-size: 0.7rem;">RUNNER-UP</small>
|
||
@elseif($index == 2)
|
||
<div class="badge rounded-pill px-3 py-2 mb-1" style="background: linear-gradient(135deg, #cd7f32, #d4943d); color: white; font-weight: 700; font-size: 0.9rem; box-shadow: 0 4px 12px rgba(205, 127, 50, 0.3);">
|
||
🥉 {{ number_format($item['nilai'], 4) }}
|
||
</div>
|
||
<small class="text-danger fw-bold" style="font-size: 0.7rem;">KETIGA</small>
|
||
@else
|
||
<div class="badge rounded-pill px-3 py-2" style="background: rgba(var(--primary-color-rgb), 0.15); color: var(--primary-color); font-weight: 700; font-size: 0.9rem;">
|
||
{{ number_format($item['nilai'], 4) }}
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
<td class="text-center">
|
||
<div class="d-flex align-items-center justify-content-center gap-2 flex-wrap" style="min-width: 0;">
|
||
<button
|
||
class="btn btn-sm btn-outline-primary rounded-pill px-3 py-1"
|
||
onclick="focusOnMarker({{ $index }})"
|
||
title="Lihat di Peta"
|
||
style="transition: all 0.2s ease; border-width: 1.5px;"
|
||
onmouseover="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 12px rgba(var(--primary-color-rgb), 0.3)'"
|
||
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow=''"
|
||
>
|
||
<i class="bi bi-pin-map-fill"></i>
|
||
<span class="d-none d-lg-inline ms-1">Peta</span>
|
||
</button>
|
||
@if(isset($item['latitude']) && isset($item['longitude']) && $item['latitude'] && $item['longitude'])
|
||
<a
|
||
href="https://www.google.com/maps/dir/?api=1&destination={{ $item['latitude'] }},{{ $item['longitude'] }}"
|
||
target="_blank"
|
||
class="btn btn-sm btn-outline-danger rounded-pill px-3 py-1"
|
||
title="Buka di Google Maps"
|
||
style="transition: all 0.2s ease; border-width: 1.5px;"
|
||
onmouseover="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 12px rgba(var(--danger-color-rgb), 0.3)'"
|
||
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow=''"
|
||
>
|
||
<i class="bi bi-google"></i>
|
||
<span class="d-none d-lg-inline ms-1">Maps</span>
|
||
</a>
|
||
@endif
|
||
@if(isset($item['no_whatsapp']) && !empty($item['no_whatsapp']))
|
||
<a
|
||
href="https://wa.me/{{ preg_replace('/[^0-9]/', '', $item['no_whatsapp']) }}"
|
||
target="_blank"
|
||
class="btn btn-sm btn-success rounded-pill px-3 py-1"
|
||
title="Chat WhatsApp"
|
||
style="transition: all 0.2s ease; border-width: 1.5px; background: #25D366; border-color: #25D366;"
|
||
onmouseover="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 12px rgba(37, 211, 102, 0.3)'"
|
||
onmouseout="this.style.transform='scale(1)'; this.style.boxShadow=''"
|
||
>
|
||
<i class="bi bi-whatsapp"></i>
|
||
<span class="d-none d-lg-inline ms-1">WA</span>
|
||
</a>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Enhanced Info & Action Buttons -->
|
||
<div class="row g-3 g-md-4">
|
||
<div class="col-12 col-lg-8">
|
||
<div class="glass-card">
|
||
<div class="card-header border-0 py-3" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--info-color-rgb), 0.15); color: var(--info-color);">
|
||
<i class="bi bi-info-circle"></i>
|
||
</div>
|
||
<h6 class="mb-0 fw-bold" style="color: var(--text-primary);">
|
||
<span class="d-none d-sm-inline">Keterangan Perhitungan SAW</span>
|
||
<span class="d-sm-none">Keterangan</span>
|
||
</h6>
|
||
</div>
|
||
</div>
|
||
<div class="card-body p-3 p-md-4">
|
||
<div class="row g-3">
|
||
<div class="col-12 col-md-6">
|
||
<div class="d-flex align-items-start mb-3">
|
||
<div class="avatar-xs d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--primary-color-rgb), 0.1); color: var(--primary-color); min-width: 24px; height: 24px;">
|
||
<i class="bi bi-1-circle" style="font-size: 0.8rem;"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); font-size: 0.9rem;">Nilai Asli</h6>
|
||
<p class="mb-0 text-muted" style="font-size: 0.85rem;">Data kriteria sebelum normalisasi</p>
|
||
</div>
|
||
</div>
|
||
<div class="d-flex align-items-start mb-3">
|
||
<div class="avatar-xs d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--info-color-rgb), 0.1); color: var(--info-color); min-width: 24px; height: 24px;">
|
||
<i class="bi bi-2-circle" style="font-size: 0.8rem;"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); font-size: 0.9rem;">Nilai Normalisasi</h6>
|
||
<p class="mb-0 text-muted" style="font-size: 0.85rem;">Nilai setelah normalisasi (badge biru)</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-12 col-md-6">
|
||
<div class="d-flex align-items-start mb-3">
|
||
<div class="avatar-xs d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--success-color-rgb), 0.1); color: var(--success-color); min-width: 24px; height: 24px;">
|
||
<i class="bi bi-3-circle" style="font-size: 0.8rem;"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); font-size: 0.9rem;">Nilai SAW</h6>
|
||
<p class="mb-0 text-muted" style="font-size: 0.85rem;">Hasil (normalisasi × bobot) semua kriteria</p>
|
||
</div>
|
||
</div>
|
||
<div class="d-flex align-items-start mb-3">
|
||
<div class="avatar-xs d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--warning-color-rgb), 0.1); color: var(--warning-color); min-width: 24px; height: 24px;">
|
||
<i class="bi bi-4-circle" style="font-size: 0.8rem;"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); font-size: 0.9rem;">Peringkat</h6>
|
||
<p class="mb-0 text-muted" style="font-size: 0.85rem;">Urutan dari nilai SAW tertinggi</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-3 pt-3" style="border-top: 1px solid rgba(0,0,0,0.1);">
|
||
<div class="d-flex align-items-center">
|
||
<div class="avatar-xs d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--danger-color-rgb), 0.1); color: var(--danger-color); min-width: 24px; height: 24px;">
|
||
<i class="bi bi-geo-alt" style="font-size: 0.8rem;"></i>
|
||
</div>
|
||
<div>
|
||
<h6 class="fw-bold mb-1" style="color: var(--text-primary); font-size: 0.9rem;">Jarak</h6>
|
||
<p class="mb-0 text-muted" style="font-size: 0.85rem;">
|
||
@if($tipe == 'kontrakan')
|
||
Dihitung otomatis dari Kampus Polije menggunakan Google Maps
|
||
@else
|
||
Dihitung otomatis dari lokasi Anda menggunakan GPS
|
||
@endif
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-12 col-lg-4">
|
||
<div class="glass-card h-100">
|
||
<div class="card-header border-0 py-3 text-center" style="background: rgba(255,255,255,0.95); backdrop-filter: blur(10px);">
|
||
<div class="d-flex align-items-center justify-content-center">
|
||
<div class="avatar-sm d-flex align-items-center justify-content-center me-2 rounded-circle" style="background: rgba(var(--warning-color-rgb), 0.15); color: var(--warning-color);">
|
||
<i class="bi bi-trophy-fill"></i>
|
||
</div>
|
||
<h6 class="mb-0 fw-bold" style="color: var(--text-primary);">Rekomendasi Terbaik</h6>
|
||
</div>
|
||
</div>
|
||
<div class="card-body text-center p-3 p-md-4">
|
||
@if(count($hasil) > 0)
|
||
@if(isset($hasil[0]['foto']) && $hasil[0]['foto'])
|
||
<img src="{{ asset('uploads/' . $hasil[0]['foto']) }}"
|
||
alt="{{ $hasil[0]['nama'] }}"
|
||
class="rounded-circle object-cover mx-auto mb-3"
|
||
style="width: 80px; height: 80px; border: 3px solid rgba(var(--warning-color-rgb), 0.3);">
|
||
@else
|
||
<div class="rounded-circle bg-gradient-warning d-flex align-items-center justify-content-center text-white fw-bold mx-auto mb-3" style="width: 80px; height: 80px; font-size: 1.5rem;">
|
||
🏆
|
||
</div>
|
||
@endif
|
||
|
||
<h6 class="fw-bold mb-2" style="color: var(--text-primary);">{{ Str::limit($hasil[0]['nama'], 25) }}</h6>
|
||
<div class="d-flex align-items-center justify-content-center text-muted mb-3" style="font-size: 0.85rem;">
|
||
<i class="bi bi-geo-alt me-1" style="color: var(--danger-color);"></i>
|
||
<span>{{ Str::limit($hasil[0]['alamat'], 30) }}</span>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<div class="badge rounded-pill px-3 py-2" style="background: linear-gradient(135deg, #ffd700, #ffed4e); color: #8B5A00; font-weight: 700; box-shadow: 0 4px 12px rgba(255, 215, 0, 0.3);">
|
||
🏆 Nilai: {{ number_format($hasil[0]['nilai'], 4) }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-grid gap-2">
|
||
@if(isset($hasil[0]['latitude']) && isset($hasil[0]['longitude']) && $hasil[0]['latitude'] && $hasil[0]['longitude'])
|
||
<a
|
||
href="https://www.google.com/maps/dir/?api=1&destination={{ $hasil[0]['latitude'] }},{{ $hasil[0]['longitude'] }}"
|
||
target="_blank"
|
||
class="btn btn-outline-danger rounded-pill"
|
||
style="transition: all 0.3s ease;"
|
||
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(var(--danger-color-rgb), 0.3)'"
|
||
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow=''"
|
||
>
|
||
<i class="bi bi-map me-2"></i>Navigasi ke Lokasi
|
||
</a>
|
||
@endif
|
||
@if(isset($hasil[0]['no_whatsapp']) && !empty($hasil[0]['no_whatsapp']))
|
||
<a
|
||
href="https://wa.me/{{ preg_replace('/[^0-9]/', '', $hasil[0]['no_whatsapp']) }}?text=Halo,%20saya%20tertarik%20dengan%20{{ urlencode($hasil[0]['nama']) }}%20dari%20hasil%20rekomendasi%20SPK.%20Apakah%20masih%20tersedia?"
|
||
target="_blank"
|
||
class="btn btn-success rounded-pill"
|
||
style="transition: all 0.3s ease; background: #25D366; border-color: #25D366;"
|
||
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(37, 211, 102, 0.3)'"
|
||
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow=''"
|
||
>
|
||
<i class="bi bi-whatsapp me-1"></i>Hubungi Pemilik
|
||
</a>
|
||
@endif
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Action Buttons -->
|
||
<div class="d-flex flex-column flex-md-row justify-content-between gap-2 mt-3 mt-md-4">
|
||
<a href="{{ route('saw.index') }}" class="btn btn-secondary btn-sm w-100 w-md-auto order-2 order-md-1">
|
||
<i class="bi bi-arrow-left me-2"></i>Hitung Ulang
|
||
</a>
|
||
<button class="btn btn-danger btn-sm w-100 w-md-auto order-1 order-md-2" onclick="window.print()">
|
||
<i class="bi bi-printer me-2"></i>Cetak Hasil
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Leaflet CSS -->
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||
|
||
<!-- Leaflet JS -->
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||
|
||
<!-- Chart.js Library -->
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||
<script>
|
||
let map;
|
||
let markers = [];
|
||
let referenceMarker = null;
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
initMap();
|
||
setTimeout(() => {
|
||
initBarChart();
|
||
initRadarChart();
|
||
}, 500);
|
||
});
|
||
|
||
function initMap() {
|
||
const hasil = @json($hasil);
|
||
const tipe = '{{ $tipe }}';
|
||
const userLat = {{ $userLat ?? 'null' }};
|
||
const userLng = {{ $userLng ?? 'null' }};
|
||
|
||
// Koordinat Kampus Polije (FIXED)
|
||
const KAMPUS_LAT = -8.15981;
|
||
const KAMPUS_LNG = 113.72312;
|
||
|
||
let centerLat = -7.797068;
|
||
let centerLng = 110.370529;
|
||
|
||
if (hasil.length > 0 && hasil[0].latitude && hasil[0].longitude) {
|
||
centerLat = hasil[0].latitude;
|
||
centerLng = hasil[0].longitude;
|
||
}
|
||
|
||
map = L.map('map').setView([centerLat, centerLng], 13);
|
||
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
attribution: '© OpenStreetMap',
|
||
maxZoom: 19
|
||
}).addTo(map);
|
||
|
||
// PENTING: Marker reference point berbeda untuk kontrakan vs laundry
|
||
if (tipe === 'kontrakan') {
|
||
// KONTRAKAN: Tampilkan marker Kampus Polije
|
||
const kampusIcon = L.divIcon({
|
||
className: 'custom-marker',
|
||
html: `<div style="background-color: #17a2b8; width: 20px; height: 20px; border-radius: 50%; border: 2px solid white; box-shadow: 0 2px 6px rgba(0,0,0,0.3);"></div>`,
|
||
iconSize: [20, 20],
|
||
iconAnchor: [10, 10]
|
||
});
|
||
|
||
referenceMarker = L.marker([KAMPUS_LAT, KAMPUS_LNG], { icon: kampusIcon })
|
||
.addTo(map)
|
||
.bindPopup(`
|
||
<div class="p-2">
|
||
<h6 class="fw-bold mb-1 small">🎓 Kampus Polije</h6>
|
||
<small class="text-muted" style="font-size: 0.75rem;">Titik acuan jarak kontrakan</small>
|
||
</div>
|
||
`);
|
||
} else {
|
||
// LAUNDRY: Tampilkan marker Lokasi User
|
||
if (userLat && userLng) {
|
||
const userIcon = L.divIcon({
|
||
className: 'custom-marker',
|
||
html: `<div style="background-color: #28a745; width: 20px; height: 20px; border-radius: 50%; border: 2px solid white; box-shadow: 0 2px 6px rgba(0,0,0,0.3);"></div>`,
|
||
iconSize: [20, 20],
|
||
iconAnchor: [10, 10]
|
||
});
|
||
|
||
referenceMarker = L.marker([userLat, userLng], { icon: userIcon })
|
||
.addTo(map)
|
||
.bindPopup(`
|
||
<div class="p-2">
|
||
<h6 class="fw-bold mb-1 small">📍 Lokasi Anda</h6>
|
||
<small class="text-muted" style="font-size: 0.75rem;">Lat: ${userLat.toFixed(6)}, Lng: ${userLng.toFixed(6)}</small>
|
||
</div>
|
||
`);
|
||
}
|
||
}
|
||
|
||
hasil.forEach((item, index) => {
|
||
if (item.latitude && item.longitude) {
|
||
addMarker(item, index);
|
||
}
|
||
});
|
||
|
||
if (markers.length > 0) {
|
||
const group = new L.featureGroup(markers.map(m => m.marker));
|
||
if (referenceMarker) group.addLayer(referenceMarker);
|
||
map.fitBounds(group.getBounds().pad(0.1));
|
||
}
|
||
}
|
||
|
||
function addMarker(item, index) {
|
||
let color = '#0d6efd';
|
||
let icon = '📍';
|
||
|
||
if (index === 0) { color = '#ffc107'; icon = '🏆'; }
|
||
else if (index === 1) { color = '#6c757d'; icon = '🥈'; }
|
||
else if (index === 2) { color = '#dc3545'; icon = '🥉'; }
|
||
|
||
const customIcon = L.divIcon({
|
||
className: 'custom-marker',
|
||
html: `
|
||
<div style="position: relative;">
|
||
<div style="background-color: ${color}; width: 24px; height: 24px; border-radius: 50%; border: 2px solid white; box-shadow: 0 2px 8px rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 12px;">${icon}</div>
|
||
<div style="position: absolute; bottom: -6px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 6px solid ${color};"></div>
|
||
</div>
|
||
`,
|
||
iconSize: [24, 30],
|
||
iconAnchor: [12, 30],
|
||
popupAnchor: [0, -30]
|
||
});
|
||
|
||
const marker = L.marker([item.latitude, item.longitude], { icon: customIcon }).addTo(map);
|
||
|
||
let waButton = '';
|
||
if (item.no_whatsapp) {
|
||
const waNumber = item.no_whatsapp.replace(/[^0-9]/g, '');
|
||
waButton = `
|
||
<a href="https://wa.me/${waNumber}"
|
||
target="_blank"
|
||
class="btn btn-sm btn-success w-100 mt-2">
|
||
<i class="bi bi-whatsapp me-1"></i>Chat WhatsApp
|
||
</a>
|
||
`;
|
||
}
|
||
|
||
const popupContent = `
|
||
<div class="p-2" style="min-width: 200px;">
|
||
<div class="d-flex align-items-center mb-2">
|
||
<span class="badge bg-${index === 0 ? 'warning' : (index === 1 ? 'secondary' : (index === 2 ? 'danger' : 'primary'))} me-2 small">
|
||
#${item.ranking}
|
||
</span>
|
||
<h6 class="fw-bold mb-0 small">${item.nama}</h6>
|
||
</div>
|
||
<p class="small text-muted mb-2" style="font-size: 0.75rem;">
|
||
<i class="bi bi-geo-alt me-1"></i>${item.alamat}
|
||
</p>
|
||
${item.no_whatsapp ? `<p class="small mb-2" style="font-size: 0.75rem;"><i class="bi bi-whatsapp text-success me-1"></i>${item.no_whatsapp}</p>` : ''}
|
||
<div class="border-top pt-2 mb-2">
|
||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||
<small class="text-muted" style="font-size: 0.75rem;">Nilai SAW:</small>
|
||
<span class="badge bg-success small">${item.nilai.toFixed(4)}</span>
|
||
</div>
|
||
</div>
|
||
<div class="d-grid gap-2">
|
||
<a href="https://www.google.com/maps/dir/?api=1&destination=${item.latitude},${item.longitude}"
|
||
target="_blank"
|
||
class="btn btn-sm btn-danger">
|
||
<i class="bi bi-google me-1"></i>Buka di Google Maps
|
||
</a>
|
||
${waButton}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
marker.bindPopup(popupContent, { maxWidth: 280, className: 'custom-popup' });
|
||
markers.push({ marker: marker, index: index, data: item });
|
||
}
|
||
|
||
function focusOnMarker(index) {
|
||
const markerData = markers.find(m => m.index === index);
|
||
if (markerData) {
|
||
map.setView(markerData.marker.getLatLng(), 16, { animate: true, duration: 1 });
|
||
markerData.marker.openPopup();
|
||
|
||
document.querySelectorAll('[id^="row-"], [id^="mobile-row-"]').forEach(row => {
|
||
row.classList.remove('table-warning', 'bg-warning', 'bg-opacity-25');
|
||
});
|
||
|
||
const row = document.getElementById(`row-${index}`);
|
||
const mobileRow = document.getElementById(`mobile-row-${index}`);
|
||
|
||
if (row) {
|
||
row.classList.add('table-warning');
|
||
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
setTimeout(() => row.classList.remove('table-warning'), 2000);
|
||
}
|
||
|
||
if (mobileRow) {
|
||
mobileRow.classList.add('bg-warning', 'bg-opacity-25');
|
||
mobileRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
setTimeout(() => mobileRow.classList.remove('bg-warning', 'bg-opacity-25'), 2000);
|
||
}
|
||
}
|
||
}
|
||
|
||
function resetMapView() {
|
||
if (markers.length > 0) {
|
||
const group = new L.featureGroup(markers.map(m => m.marker));
|
||
if (referenceMarker) group.addLayer(referenceMarker);
|
||
map.fitBounds(group.getBounds().pad(0.1), { animate: true, duration: 1 });
|
||
}
|
||
}
|
||
|
||
function initBarChart() {
|
||
const hasil = @json($hasil);
|
||
const top10 = hasil.slice(0, 10);
|
||
|
||
const labels = top10.map(item => item.nama.length > 15 ? item.nama.substring(0, 15) + '...' : item.nama);
|
||
const values = top10.map(item => item.nilai);
|
||
|
||
const backgroundColors = top10.map((item, index) => {
|
||
if (index === 0) return 'rgba(255, 193, 7, 0.8)';
|
||
if (index === 1) return 'rgba(108, 117, 125, 0.8)';
|
||
if (index === 2) return 'rgba(220, 53, 69, 0.8)';
|
||
return 'rgba(13, 110, 253, 0.6)';
|
||
});
|
||
|
||
const ctx = document.getElementById('barChartSAW');
|
||
if (!ctx) return;
|
||
|
||
new Chart(ctx, {
|
||
type: 'bar',
|
||
data: {
|
||
labels: labels,
|
||
datasets: [{
|
||
label: 'Nilai SAW',
|
||
data: values,
|
||
backgroundColor: backgroundColors,
|
||
borderColor: backgroundColors.map(c => c.replace('0.6)', '1)').replace('0.8)', '1)')),
|
||
borderWidth: 2,
|
||
borderRadius: 6,
|
||
borderSkipped: false,
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: true,
|
||
plugins: {
|
||
legend: { display: false },
|
||
tooltip: {
|
||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||
padding: 10,
|
||
titleFont: { size: 12, weight: 'bold' },
|
||
bodyFont: { size: 11 },
|
||
callbacks: {
|
||
title: function(context) {
|
||
const index = context[0].dataIndex;
|
||
let medal = '';
|
||
if (index === 0) medal = '🏆 ';
|
||
if (index === 1) medal = '🥈 ';
|
||
if (index === 2) medal = '🥉 ';
|
||
return medal + 'Peringkat #' + (index + 1);
|
||
},
|
||
label: function(context) {
|
||
return 'Nilai: ' + context.parsed.y.toFixed(4);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true,
|
||
grid: { color: 'rgba(0, 0, 0, 0.05)' },
|
||
ticks: { font: { size: 10 } }
|
||
},
|
||
x: {
|
||
grid: { display: false },
|
||
ticks: { font: { size: 9 }, maxRotation: 45, minRotation: 45 }
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function initRadarChart() {
|
||
const hasil = @json($hasil);
|
||
const kriteria = @json($kriteria);
|
||
const top3 = hasil.slice(0, 3);
|
||
|
||
if (top3.length === 0) return;
|
||
|
||
const labels = kriteria.map(k => k.nama_kriteria.length > 10 ? k.nama_kriteria.substring(0, 10) + '...' : k.nama_kriteria);
|
||
|
||
const datasets = top3.map((item, index) => {
|
||
const data = kriteria.map(k => {
|
||
const norm = item.normalisasi[k.nama_kriteria];
|
||
return norm ? norm.normalisasi : 0;
|
||
});
|
||
|
||
let color = 'rgba(13, 110, 253, 0.6)';
|
||
let borderColor = 'rgba(13, 110, 253, 1)';
|
||
let label = item.nama;
|
||
|
||
if (index === 0) {
|
||
color = 'rgba(255, 193, 7, 0.3)';
|
||
borderColor = 'rgba(255, 193, 7, 1)';
|
||
label = '🏆 ' + (item.nama.length > 12 ? item.nama.substring(0, 12) + '...' : item.nama);
|
||
} else if (index === 1) {
|
||
color = 'rgba(108, 117, 125, 0.3)';
|
||
borderColor = 'rgba(108, 117, 125, 1)';
|
||
label = '🥈 ' + (item.nama.length > 12 ? item.nama.substring(0, 12) + '...' : item.nama);
|
||
} else if (index === 2) {
|
||
color = 'rgba(220, 53, 69, 0.3)';
|
||
borderColor = 'rgba(220, 53, 69, 1)';
|
||
label = '🥉 ' + (item.nama.length > 12 ? item.nama.substring(0, 12) + '...' : item.nama);
|
||
}
|
||
|
||
return {
|
||
label: label,
|
||
data: data,
|
||
backgroundColor: color,
|
||
borderColor: borderColor,
|
||
borderWidth: 2,
|
||
pointBackgroundColor: borderColor,
|
||
pointBorderColor: '#fff',
|
||
pointRadius: 3,
|
||
pointHoverRadius: 5
|
||
};
|
||
});
|
||
|
||
const ctx = document.getElementById('radarChartTop3');
|
||
if (!ctx) return;
|
||
|
||
new Chart(ctx, {
|
||
type: 'radar',
|
||
data: { labels: labels, datasets: datasets },
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: true,
|
||
plugins: {
|
||
legend: {
|
||
display: true,
|
||
position: 'bottom',
|
||
labels: { font: { size: 9 }, padding: 10, usePointStyle: true }
|
||
}
|
||
},
|
||
scales: {
|
||
r: {
|
||
beginAtZero: true,
|
||
max: 1,
|
||
ticks: { stepSize: 0.2, font: { size: 8 } },
|
||
pointLabels: { font: { size: 9, weight: 'bold' } }
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.table tbody tr {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.table tbody tr:hover {
|
||
transform: scale(1.005);
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.custom-marker {
|
||
background: none;
|
||
border: none;
|
||
}
|
||
|
||
.custom-popup .leaflet-popup-content-wrapper {
|
||
border-radius: 8px;
|
||
padding: 0;
|
||
}
|
||
|
||
.table-warning, .bg-warning.bg-opacity-25 {
|
||
animation: highlight 0.5s ease-in-out;
|
||
}
|
||
|
||
@keyframes highlight {
|
||
0%, 100% { background-color: transparent; }
|
||
50% { background-color: #fff3cd; }
|
||
}
|
||
|
||
.progress {
|
||
background-color: #f0f0f0;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.progress-bar {
|
||
transition: width 1s ease-in-out;
|
||
animation: progressAnimation 1.5s ease-out;
|
||
}
|
||
|
||
@keyframes progressAnimation {
|
||
from { width: 0%; }
|
||
}
|
||
|
||
@media (max-width: 767px) {
|
||
#map {
|
||
height: 250px !important;
|
||
}
|
||
|
||
canvas {
|
||
max-height: 250px !important;
|
||
}
|
||
|
||
.badge {
|
||
font-size: 0.75rem;
|
||
padding: 0.25em 0.5em;
|
||
}
|
||
|
||
.btn-sm {
|
||
font-size: 0.8rem;
|
||
padding: 0.35rem 0.6rem;
|
||
}
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
#map {
|
||
height: 400px !important;
|
||
}
|
||
|
||
canvas {
|
||
max-height: 350px !important;
|
||
}
|
||
}
|
||
|
||
@media print {
|
||
.btn, .breadcrumb, nav, #map, .card-footer {
|
||
display: none !important;
|
||
}
|
||
|
||
.card {
|
||
box-shadow: none !important;
|
||
border: 1px solid #dee2e6 !important;
|
||
}
|
||
|
||
canvas {
|
||
max-height: 250px !important;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// Handle Export Buttons
|
||
document.getElementById('btnExcelSAW').addEventListener('click', function() {
|
||
const hasil = {!! json_encode($hasil) !!};
|
||
document.querySelector('#formExcelSAW input[name="hasil_json"]').value = JSON.stringify(hasil);
|
||
document.getElementById('formExcelSAW').submit();
|
||
});
|
||
|
||
document.getElementById('btnPdfSAW').addEventListener('click', function() {
|
||
const hasil = {!! json_encode($hasil) !!};
|
||
document.querySelector('#formPdfSAW input[name="hasil_json"]').value = JSON.stringify(hasil);
|
||
document.getElementById('formPdfSAW').submit();
|
||
});
|
||
</script>
|
||
@endsection
|