439 lines
17 KiB
Plaintext
439 lines
17 KiB
Plaintext
@{
|
|
var jenis = "Komposting";
|
|
var tableUrl = Url.Content("~/ReduksiSampah/Komposting/Table");
|
|
ViewData["Title"] = $"Reduksi Sampah - {jenis}";
|
|
}
|
|
|
|
<div class="flex flex-col gap-2 md:flex-row md:justify-between md:gap-0 items-center">
|
|
<div class="prose md:w-1/2 w-full">
|
|
<span class="text-xl font-semibold text-black">
|
|
Reduksi Sampah - @jenis
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-2 md:w-1/2 w-full justify-end">
|
|
<select id="filter_tahun" class="select md:w-44 w-full rounded-full select-sm">
|
|
<option value="">Semua Tahun</option>
|
|
<option value="2025">2025</option>
|
|
<option value="2024">2024</option>
|
|
<option value="2023">2023</option>
|
|
</select>
|
|
<button class="btn btn-sm rounded-full bg-bank-sampah-primary-500 text-white hover:bg-bank-sampah-primary-600"
|
|
onclick="modal_tambah.showModal()">
|
|
<i class="ph ph-plus"></i>
|
|
Tambah Lokasi
|
|
</button>
|
|
<button class="btn btn-sm rounded-full btn-neutral" onclick="modal_validasi.showModal()">
|
|
<i class="ph ph-check-circle"></i>
|
|
Validasi Data
|
|
</button>
|
|
<button class="btn btn-sm rounded-full btn-info" onclick="modal_history.showModal()">
|
|
<i class="ph ph-clock-counter-clockwise"></i>
|
|
History
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="h-6"></div>
|
|
|
|
<!-- Modal Tambah -->
|
|
<dialog id="modal_tambah" class="modal modal-bottom sm:modal-middle">
|
|
<div class="modal-box w-full max-w-3xl p-6 bg-white rounded-2xl">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h3 class="text-lg font-semibold text-gray-900">Tambah Lokasi Komposting</h3>
|
|
<form method="dialog">
|
|
<button class="btn btn-sm btn-circle btn-ghost">✕</button>
|
|
</form>
|
|
</div>
|
|
<form class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Nama Lokasi</legend>
|
|
<input type="text" class="input w-full" placeholder="Nama Lokasi" />
|
|
</fieldset>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Periode</legend>
|
|
<input type="month" class="input w-full" />
|
|
</fieldset>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Kecamatan</legend>
|
|
<input type="text" class="input w-full" placeholder="Kecamatan" />
|
|
</fieldset>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Kelurahan</legend>
|
|
<input type="text" class="input w-full" placeholder="Kelurahan" />
|
|
</fieldset>
|
|
</div>
|
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">RT</legend>
|
|
<input type="text" class="input w-full" placeholder="RT" />
|
|
</fieldset>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">RW</legend>
|
|
<input type="text" class="input w-full" placeholder="RW" />
|
|
</fieldset>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Status</legend>
|
|
<select class="select w-full">
|
|
<option>Aktif</option>
|
|
<option>Tidak Aktif</option>
|
|
</select>
|
|
</fieldset>
|
|
</div>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Bukti</legend>
|
|
<input type="file" class="file-input w-full" />
|
|
</fieldset>
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Detail Alamat</legend>
|
|
<textarea class="textarea w-full" rows="3"></textarea>
|
|
</fieldset>
|
|
<div class="flex justify-end gap-2 pt-4">
|
|
<button type="button" class="btn btn-ghost rounded-full" onclick="modal_tambah.close()">Batal</button>
|
|
<button type="submit" class="btn bg-bank-sampah-primary-500 text-white rounded-full">Simpan</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- Modal Validasi -->
|
|
<dialog id="modal_validasi" class="modal modal-bottom sm:modal-middle">
|
|
<div class="modal-box w-full max-w-xl p-6 bg-white rounded-2xl">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">Validasi Data Komposting</h3>
|
|
<form method="dialog">
|
|
<button class="btn btn-sm btn-circle btn-ghost">✕</button>
|
|
</form>
|
|
</div>
|
|
<div class="space-y-4">
|
|
<div class="bg-gray-50 rounded-xl p-4">
|
|
<div class="text-sm text-gray-500">Periode</div>
|
|
<div class="text-base font-semibold text-gray-900">Jan 2025</div>
|
|
<div class="mt-2 text-sm text-gray-500">Nama Lokasi</div>
|
|
<div class="text-base font-semibold text-gray-900">Jakarta Pusat</div>
|
|
</div>
|
|
<div class="flex flex-col sm:flex-row gap-3">
|
|
<button class="btn btn-outline btn-error rounded-full w-full sm:w-1/2" type="button">
|
|
Tolak
|
|
</button>
|
|
<button class="btn btn-success text-white rounded-full w-full sm:w-1/2" type="button">
|
|
Setujui
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- Modal History -->
|
|
<dialog id="modal_history" class="modal modal-bottom sm:modal-middle">
|
|
<div class="modal-box w-full max-w-xl p-6 bg-white rounded-2xl">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">History Perubahan Data</h3>
|
|
<form method="dialog">
|
|
<button class="btn btn-sm btn-circle btn-ghost">✕</button>
|
|
</form>
|
|
</div>
|
|
<div class="max-h-80 overflow-y-auto">
|
|
<ul class="timeline timeline-vertical">
|
|
<li>
|
|
<div class="timeline-start">Hari ini</div>
|
|
<div class="timeline-middle">
|
|
<i class="ph ph-check-circle text-green-600"></i>
|
|
</div>
|
|
<div class="timeline-end timeline-box">
|
|
Validasi data oleh Admin Subandri
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<div class="timeline-start">3 hari lalu</div>
|
|
<div class="timeline-middle">
|
|
<i class="ph ph-pen text-amber-500"></i>
|
|
</div>
|
|
<div class="timeline-end timeline-box">
|
|
Pembaruan data B3 dan B4
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<div class="timeline-start">1 minggu lalu</div>
|
|
<div class="timeline-middle">
|
|
<i class="ph ph-plus-circle text-blue-500"></i>
|
|
</div>
|
|
<div class="timeline-end timeline-box">
|
|
Penambahan lokasi baru Jakarta Utara
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
|
|
<div class="card bg-white shadow-sm">
|
|
<div class="card-body p-2">
|
|
<div class="w-full overflow-x-auto">
|
|
<table class="table w-full text-center" id="example">
|
|
<thead>
|
|
<tr>
|
|
<th rowspan="2">No</th>
|
|
<th colspan="5">Lokasi Rumah Komposting</th>
|
|
<th rowspan="2">Status</th>
|
|
<th colspan="12">Jumlah Sampah yang di Reduksi (Kg/Bulan)</th>
|
|
<th rowspan="2">Aksi</th>
|
|
</tr>
|
|
<tr>
|
|
<th>Kecamatan</th>
|
|
<th>Kelurahan</th>
|
|
<th>RT</th>
|
|
<th>RW</th>
|
|
<th>Detail Alamat</th>
|
|
<th>B1</th>
|
|
<th>B2</th>
|
|
<th>B3</th>
|
|
<th>B4</th>
|
|
<th>B5</th>
|
|
<th>B6</th>
|
|
<th>B7</th>
|
|
<th>B8</th>
|
|
<th>B9</th>
|
|
<th>B10</th>
|
|
<th>B11</th>
|
|
<th>B12</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<script type="text/javascript">
|
|
const tableAjaxUrl = '@tableUrl';
|
|
const updateUrl = '@Url.Content("~/ReduksiSampah/Komposting/Update")';
|
|
const decimalFields = ['b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10', 'b11', 'b12'];
|
|
let table;
|
|
|
|
$(document).ready(function () {
|
|
const columns = [
|
|
{ data: null, render: function (data, type, row, meta) { return meta.row + 1; }, orderable: false, searchable: false },
|
|
{ data: 'kecamatan', defaultContent: '-' },
|
|
{ data: 'kelurahan', defaultContent: '-' },
|
|
{ data: 'rt', defaultContent: '-' },
|
|
{ data: 'rw', defaultContent: '-' },
|
|
{ data: 'detail_alamat', defaultContent: '-' },
|
|
{
|
|
data: 'status',
|
|
render: function (data, type, row) {
|
|
return buildStatusSelect(row.id, data);
|
|
},
|
|
orderable: false,
|
|
searchable: false
|
|
}
|
|
];
|
|
|
|
decimalFields.forEach(function (field) {
|
|
columns.push({
|
|
data: field,
|
|
defaultContent: null,
|
|
render: function (data, type, row) {
|
|
return buildDecimalInput(row.id, field, data);
|
|
},
|
|
orderable: false,
|
|
searchable: false
|
|
});
|
|
});
|
|
|
|
columns.push({
|
|
data: null,
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function (data, type, row) {
|
|
return `<div class="flex items-center justify-center gap-2">
|
|
<button type="button"
|
|
class="btn btn-square btn-sm btn-warning text-white btn-edit-row tooltip tooltip-left tooltip-warning"
|
|
data-tip="Edit"
|
|
data-id="${row.id}">
|
|
<i class="ph ph-note-pencil"></i>
|
|
</button>
|
|
<button type="button"
|
|
class="btn btn-square btn-sm btn-success text-white btn-save-row tooltip tooltip-left tooltip-success"
|
|
data-id="${row.id}"
|
|
data-tip="Simpan"
|
|
disabled>
|
|
<i class="ph ph-floppy-disk"></i>
|
|
</button>
|
|
</div>`;
|
|
}
|
|
});
|
|
|
|
table = new DataTable('#example', {
|
|
ajax: {
|
|
url: tableAjaxUrl,
|
|
data: function (d) {
|
|
d.tahun = $('#filter_tahun').val();
|
|
},
|
|
dataSrc: 'data',
|
|
error: function (xhr, status, err) {
|
|
console.error('Gagal memuat data komposting:', err);
|
|
}
|
|
},
|
|
scrollX: true,
|
|
scrollCollapse: true,
|
|
autoWidth: false,
|
|
searching: false,
|
|
ordering: false,
|
|
paging: false,
|
|
info: false,
|
|
columns: columns,
|
|
fixedColumns: {
|
|
left: 6
|
|
},
|
|
createdRow: function (row, data) {
|
|
$(row).attr('data-row-id', data.id);
|
|
},
|
|
initComplete: function () {
|
|
$('div.dt-scroll-body thead').css('visibility', 'collapse');
|
|
}
|
|
});
|
|
|
|
$('#example').on('click', '.btn-edit-row', function () {
|
|
const rowId = $(this).data('id');
|
|
setRowEditingState(rowId, true);
|
|
});
|
|
|
|
$('#example').on('click', '.btn-save-row', function () {
|
|
const rowId = $(this).data('id');
|
|
saveRowChanges(rowId);
|
|
});
|
|
|
|
$('#filter_tahun').on('change', function () {
|
|
table.ajax.reload();
|
|
});
|
|
});
|
|
|
|
function buildStatusSelect(rowId, currentValue) {
|
|
const options = ['Aktif', 'Tidak Aktif']
|
|
.map(function (option) {
|
|
const selected = option === currentValue ? 'selected' : '';
|
|
return `<option value="${option}" ${selected}>${option}</option>`;
|
|
})
|
|
.join('');
|
|
|
|
return `<div class="flex justify-center">
|
|
<select class="select select-warning select-sm w-32 text-black"
|
|
data-field="status"
|
|
data-id="${rowId}"
|
|
name="status-${rowId}"
|
|
disabled>
|
|
${options}
|
|
</select>
|
|
</div>`;
|
|
}
|
|
|
|
function buildDecimalInput(rowId, fieldName, value) {
|
|
const displayValue = (typeof value === 'number' || typeof value === 'string') ? value : '';
|
|
|
|
return `<div class="flex justify-center">
|
|
<input type="number"
|
|
step="0.01"
|
|
min="0"
|
|
data-id="${rowId}"
|
|
data-field="${fieldName}"
|
|
name="${fieldName}-${rowId}"
|
|
value="${displayValue}"
|
|
class="input input-warning input-sm w-24 text-right text-black"
|
|
disabled />
|
|
</div>`;
|
|
}
|
|
|
|
function getRowById(rowId) {
|
|
return $(`#example tbody tr[data-row-id="${rowId}"]`);
|
|
}
|
|
|
|
function setRowEditingState(rowId, enableEditing) {
|
|
const $row = getRowById(rowId);
|
|
if ($row.length === 0) {
|
|
return;
|
|
}
|
|
|
|
$row.toggleClass('editing', enableEditing);
|
|
$row.find('select[data-field], input[data-field]').prop('disabled', !enableEditing);
|
|
$row.find('.btn-edit-row').prop('disabled', enableEditing);
|
|
$row.find('.btn-save-row').prop('disabled', !enableEditing);
|
|
|
|
if (enableEditing) {
|
|
const $firstInput = $row.find('input[data-field]').first();
|
|
if ($firstInput.length) {
|
|
$firstInput.trigger('focus');
|
|
}
|
|
}
|
|
}
|
|
|
|
function collectRowPayload(rowId) {
|
|
const id = parseInt(rowId, 10);
|
|
if (Number.isNaN(id)) {
|
|
return null;
|
|
}
|
|
|
|
const $row = getRowById(rowId);
|
|
if ($row.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const payload = {
|
|
Id: id,
|
|
Status: $row.find('select[data-field="status"]').val()
|
|
};
|
|
|
|
decimalFields.forEach(function (field) {
|
|
const $input = $row.find(`input[data-field="${field}"]`);
|
|
const rawValue = ($input.val() || '').trim();
|
|
payload[field.toUpperCase()] = rawValue === '' ? null : parseFloat(rawValue);
|
|
});
|
|
|
|
return payload;
|
|
}
|
|
|
|
function saveRowChanges(rowId) {
|
|
const payload = collectRowPayload(rowId);
|
|
if (!payload) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal',
|
|
text: 'Data tidak valid untuk disimpan.'
|
|
});
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: updateUrl,
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(payload),
|
|
success: function (response) {
|
|
const successMessage = (response && response.message) ? response.message : 'Data berhasil disimpan.';
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Berhasil',
|
|
text: successMessage
|
|
}).then(function () {
|
|
setRowEditingState(rowId, false);
|
|
table.ajax.reload(null, false);
|
|
});
|
|
},
|
|
error: function (xhr) {
|
|
const message = (xhr && xhr.responseJSON && xhr.responseJSON.message)
|
|
? xhr.responseJSON.message
|
|
: 'Terjadi kesalahan saat menyimpan data.';
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal',
|
|
text: message
|
|
});
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
} |