MIF_E31222658/resources/views/admin/kriteria/index.blade.php

535 lines
21 KiB
PHP

@extends('layouts.app')
@section('title', 'Politeknik Negeri Jember - Manajemen Kriteria')
@section('topbar', 'Data Kriteria')
@section('css')
<link href="{{ asset('vendor/datatables/dataTables.bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ asset('vendor/fontawesome-free/css/all.min.css') }}" rel="stylesheet">
<style>
:root {
--primary:rgb(0, 122, 153);
--secondary: #858796;
--success: #1cc88a;
--danger: #e74a3b;
--warning: #f6c23e;
--light: #f8f9fc;
}
.card {
border: none;
border-radius: 0.5rem;
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
transition: all 0.3s;
}
.card:hover {
box-shadow: 0 0.5rem 2rem 0 rgba(58, 59, 69, 0.2);
}
.card-header {
background-color: var(--primary);
color: white;
border-radius: 0.5rem 0.5rem 0 0 !important;
padding: 1rem 1.5rem;
}
.btn-circle {
width: 35px;
height: 35px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0 3px;
transition: all 0.3s;
}
.btn-circle:hover {
transform: translateY(-2px);
}
.form-control {
border-radius: 0.35rem;
padding: 0.75rem 1rem;
border: 1px solid #d1d3e2;
}
.form-control:focus {
border-color: var(--primary);
box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25);
}
.badge-benefit {
background-color: rgba(28, 200, 138, 0.2);
color: var(--success);
}
.badge-cost {
background-color: rgba(231, 74, 59, 0.2);
color: var(--danger);
}
.table {
border-collapse: separate;
border-spacing: 0 0.75rem;
}
.table thead th {
border: none;
background-color: #f8f9fc;
color: #5a5c69;
font-weight: 600;
text-transform: uppercase;
font-size: 0.75rem;
}
.table tbody tr {
background-color: white;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
transition: all 0.3s;
}
.table tbody tr:hover {
transform: translateY(-2px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
}
.table tbody td {
vertical-align: middle;
border-top: none;
padding: 1rem;
}
.table tbody td:first-child {
border-radius: 0.35rem 0 0 0.35rem;
}
.table tbody td:last-child {
border-radius: 0 0.35rem 0.35rem 0;
}
.pagination .page-item.active .page-link {
background-color: var(--primary);
border-color: var(--primary);
}
.pagination .page-link {
color: var(--primary);
}
.invalid-feedback {
display: block;
margin-top: 0.25rem;
font-size: 0.875rem;
color: var(--danger);
}
.is-invalid {
border-color: var(--danger) !important;
}
.alert {
border-radius: 0.35rem;
border: none;
}
.download-btn {
transition: all 0.3s;
}
.download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
}
</style>
@endsection
@php
use App\Models\Kriteria;
@endphp
@section('content')
<div class="container-fluid">
<!-- Error Alert -->
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<div class="d-flex align-items-center">
<i class="fas fa-exclamation-circle mr-3"></i>
<div>
<strong>Error!</strong> {{ session('error') }}
</div>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
<div class="row">
<!-- Form Tambah Kriteria -->
<div class="col-lg-4">
<div class="card mb-4">
<div class="card-header d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-white">
<i class="fas fa-plus-circle mr-2"></i>Tambah Kriteria
</h6>
</div>
<div class="card-body">
<!-- Notifikasi Total Bobot -->
<div class="alert alert-warning d-flex align-items-center" role="alert">
<i class="fas fa-info-circle mr-2"></i>
<div>
<strong>Perhatian:</strong> Total bobot seluruh kriteria harus berjumlah <strong>1</strong>.
</div>
</div>
{{-- Notifikasi sukses --}}
@if (session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle mr-2"></i> {{ session('success') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span>&times;</span>
</button>
</div>
@endif
{{-- Notifikasi warning --}}
@if (session('warning'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle mr-2"></i> {!! session('warning') !!}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span>&times;</span>
</button>
</div>
@endif
<form id="kriteriaForm" action="{{ route('kriteria.store') }}" method="post">
@csrf
<div class="form-group">
<label for="nama_kriteria" class="font-weight-bold">Nama Kriteria</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text bg-light">
<i class="fas fa-tag text-primary"></i>
</span>
</div>
<input type="text" class="form-control" name="nama_kriteria"
value="{{ old('nama_kriteria') }}"
placeholder="Masukkan nama kriteria"
required>
@if($errors->has('nama_kriteria'))
<div class="invalid-feedback d-block">{{ $errors->first('nama_kriteria') }}</div>
@endif
</div>
<div class="invalid-feedback"></div>
</div>
<div class="form-group">
<label for="attribut" class="font-weight-bold">Attribut Kriteria</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text bg-light">
<i class="fas fa-chart-line text-primary"></i>
</span>
</div>
<select name="attribut" class="form-control py-2" required>
<option value="benefit" {{ old('attribut') == 'benefit' ? 'selected' : '' }}>benefit</option>
<option value="cost" {{ old('attribut') == 'cost' ? 'selected' : '' }}>cost</option>
</select>
</div>
<div class="invalid-feedback"></div>
</div>
<div class="form-group">
<label for="bobot" class="font-weight-bold">Bobot Kriteria (0-1)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text bg-light">
<i class="fas fa-balance-scale text-primary"></i>
</span>
</div>
<input type="number" step="0.01" min="0" max="1"
class="form-control" name="bobot" id="bobotInput"
value="{{ old('bobot') }}"
placeholder="0.00 - 1.00"
required>
</div>
<div class="invalid-feedback"></div>
<small id="bobotInfo" class="form-text text-muted mt-1"></small>
</div>
<div class="form-group text-right mt-4">
<button type="reset" class="btn btn-outline-secondary mr-2" id="resetBtn">
<i class="fas fa-redo mr-2"></i>Reset
</button>
<button type="submit" class="btn btn-primary px-4" id="saveButton">
<i class="fas fa-save mr-2"></i>Simpan
</button>
</div>
<input type="hidden" name="confirmed" id="confirmed" value="0">
</form>
</div>
</div>
</div>
<!-- Tabel List Kriteria -->
<div class="col-lg-8">
<div class="card mb-4">
<div class="card-header d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-white">
<i class="fas fa-list-alt mr-2"></i>Daftar Kriteria
</h6>
<a href="{{ url('kriteria/download/pdf') }}" class="btn btn-light download-btn">
<i class="fas fa-file-pdf mr-2"></i>Download PDF
</a>
</div>
<div class="card-body">
{{-- Notifikasi Total Bobot --}}
<div class="mb-3">
@php
$totalBobot = round(Kriteria::sum('bobot'), 4);
$diff = 1 - $totalBobot;
@endphp
@if ($totalBobot < 1)
<div class="alert alert-warning mt-3">
⚠️ Total Bobot Saat Ini: <strong>{{ number_format($totalBobot, 2) }}</strong><br>
Total bobot masih kurang sebesar <strong>{{ number_format(abs($diff), 2) }}</strong>.
</div>
@elseif ($totalBobot > 1)
<div class="alert alert-danger mt-3">
Total Bobot Saat Ini: <strong>{{ number_format($totalBobot, 2) }}</strong><br>
Total bobot melebihi batas maksimal. Kelebihan sebesar <strong>{{ number_format(abs($diff), 2) }}</strong>.
</div>
@else
<div class="alert alert-success mt-3">
Total Bobot Sudah Tepat: <strong>{{ $totalBobot }}</strong>
</div>
@endif
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table" id="DataTable">
<thead>
<tr>
<th width="5%">No</th>
<th>Nama Kriteria</th>
<th width="15%">Attribut</th>
<th width="15%">Bobot</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody>
@php $no = 1; @endphp
@foreach ($kriteria as $row)
<tr>
<td class="text-center">{{ $no++ }}</td>
<td>{{ $row->nama_kriteria }}</td>
<td>
<span class="badge badge-pill {{ $row->attribut == 'benefit' ? 'badge-benefit' : 'badge-cost' }}">
{{ ucfirst($row->attribut) }}
</span>
</td>
<td>{{ number_format($row->bobot, 2) }}</td>
<td class="text-center">
<a href="{{ route('kriteria.edit', $row->id) }}"
class="btn btn-circle btn-warning"
data-toggle="tooltip"
title="Edit">
<i class="fas fa-edit"></i>
</a>
<button class="btn btn-circle btn-danger delete-btn"
data-id="{{ $row->id }}"
data-url="{{ route('kriteria.destroy', $row->id) }}"
title="Hapus">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('js')
<!-- Page level plugins -->
<script src="{{ asset('vendor/jquery/jquery.min.js') }}"></script>
<script src="{{ asset('vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('vendor/datatables/jquery.dataTables.min.js') }}"></script>
<script src="{{ asset('vendor/datatables/dataTables.bootstrap4.min.js') }}"></script>
<script src="{{ asset('js/sweetalert.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
$(document).ready(function () {
let currentTotal = {{ $totalBobot ?? 0 }};
let isConfirmed = false;
// Validasi ketika input bobot berubah
$('#bobotInput').on('input', function () {
const newBobot = parseFloat($(this).val()) || 0;
const total = currentTotal + newBobot;
const roundedTotal = parseFloat(total.toFixed(2));
let message = '';
if (roundedTotal > 1) {
message = `⚠️ Total bobot akan menjadi ${roundedTotal} (melebihi 1)`;
$('#bobotInfo').removeClass().addClass('form-text text-danger').text(message);
} else if (roundedTotal < 1) {
message = `⚠️ Total bobot akan menjadi ${roundedTotal} (belum 1)`;
$('#bobotInfo').removeClass().addClass('form-text text-warning').text(message);
} else {
message = `✅ Total bobot akan menjadi 1 (tepat)`;
$('#bobotInfo').removeClass().addClass('form-text text-success').text(message);
}
});
// Reset form
$('button[type="reset"]').on('click', function () {
$('#kriteriaForm')[0].reset();
$('#bobotInfo').text('').removeClass();
isConfirmed = false;
});
// Handler submit form
$('#kriteriaForm').on('submit', function (e) {
if (isConfirmed) return true;
e.preventDefault();
const newBobot = parseFloat($('#bobotInput').val()) || 0;
const total = parseFloat((currentTotal + newBobot).toFixed(2));
if (total > 1 || total < 1) {
let pesan = total > 1
? `Total bobot akan menjadi <strong>${total.toFixed(2)}</strong> (melebihi 1)`
: `Total bobot akan menjadi <strong>${total.toFixed(2)}</strong> (kurang dari 1)`;
Swal.fire({
title: 'Konfirmasi Total Bobot',
html: pesan + '<br>Apakah Anda yakin ingin menyimpan?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, simpan!',
cancelButtonText: 'Batal',
confirmButtonColor: '#1cc88a',
cancelButtonColor: '#e74a3b'
}).then((result) => {
if (result.isConfirmed) {
isConfirmed = true;
$('#confirmed').val(1);
$('#kriteriaForm')[0].submit();
}
});
} else {
// Total bobot sudah tepat, langsung submit
isConfirmed = true;
$('#confirmed').val(1);
this.submit();
}
});
// Inisialisasi DataTable
var table = $('#DataTable').DataTable({
paging: false,
searching: true,
ordering: true,
info: false,
language: {
search: "Cari:",
zeroRecords: "Tidak ada data yang ditemukan",
infoEmpty: "Tidak ada data yang tersedia",
},
drawCallback: function () {
initNavbarDropdown();
}
});
// Navbar Dropdown
function initNavbarDropdown() {
$(document).off('click.navbarDropdown');
$(document).on('click.navbarDropdown', '.dropdown-toggle', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
var $dropdown = $(this).closest('.dropdown');
var isShown = $dropdown.hasClass('show');
$('.dropdown').removeClass('show');
$('.dropdown-menu').removeClass('show');
if (!isShown) {
$dropdown.addClass('show');
$dropdown.find('.dropdown-menu').addClass('show');
}
return false;
});
$(document).on('click.navbarDropdown', function (e) {
if (!$(e.target).closest('.dropdown').length) {
$('.dropdown').removeClass('show');
$('.dropdown-menu').removeClass('show');
}
});
}
initNavbarDropdown();
// Delete handler
$('.delete-btn').click(function (e) {
e.preventDefault();
const url = $(this).data('url');
Swal.fire({
title: 'Apakah Anda yakin?',
text: "Data yang dihapus tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: url,
type: 'POST',
data: {
'_method': 'DELETE',
'_token': '{{ csrf_token() }}'
},
success: function (response) {
Swal.fire({
title: 'Terhapus!',
text: 'Data berhasil dihapus.',
icon: 'success',
timer: 1500,
showConfirmButton: false
}).then(() => {
location.reload();
});
},
error: function (xhr) {
Swal.fire({
title: 'Gagal!',
text: 'Terjadi kesalahan saat menghapus data.',
icon: 'error',
confirmButtonText: 'OK'
});
}
});
}
});
});
});
</script>
@endsection