refactor: menu maggot & komposting

main
Yuri Dimas 2025-12-24 15:18:15 +07:00
parent 0f1b8f5235
commit 34c1cd289b
No known key found for this signature in database
GPG Key ID: 9FD7E44BC294C68C
7 changed files with 1070 additions and 731 deletions

View File

@ -14,14 +14,47 @@ namespace BankSampahApp.Controllers.ReduksiSampah
}
[HttpGet]
public IActionResult Table([FromQuery] string? bulan)
public IActionResult Table([FromQuery] string? bulan, [FromQuery] string? tahun)
{
var data = ReduksiSampahDataStore
.GetItems(Jenis)
.Where(item => ReduksiSampahDataStore.MatchesBulan(item, bulan))
.Select(ReduksiSampahDataStore.ToResponse);
.Where(item => ReduksiSampahDataStore.MatchesPeriode(item, bulan, tahun))
.Select(ReduksiSampahDataStore.ToDetailedResponse);
return Json(new { data });
}
[HttpPost]
public IActionResult Update([FromBody] MaggotController.MaggotUpdateRequest request)
{
if (request == null)
{
return BadRequest(new { message = "Permintaan tidak valid." });
}
var updated = ReduksiSampahDataStore.UpdateReduksiValues(
Jenis,
request.Id,
request.Status,
request.B1,
request.B2,
request.B3,
request.B4,
request.B5,
request.B6,
request.B7,
request.B8,
request.B9,
request.B10,
request.B11,
request.B12);
if (!updated)
{
return NotFound(new { message = "Data komposting tidak ditemukan." });
}
return Ok(new { message = "Data komposting berhasil diperbarui." });
}
}
}

View File

@ -14,14 +14,65 @@ namespace BankSampahApp.Controllers.ReduksiSampah
}
[HttpGet]
public IActionResult Table([FromQuery] string? bulan)
public IActionResult Table([FromQuery] string? bulan, [FromQuery] string? tahun)
{
var data = ReduksiSampahDataStore
.GetItems(Jenis)
.Where(item => ReduksiSampahDataStore.MatchesBulan(item, bulan))
.Select(ReduksiSampahDataStore.ToResponse);
.Where(item => ReduksiSampahDataStore.MatchesPeriode(item, bulan, tahun))
.Select(ReduksiSampahDataStore.ToDetailedResponse);
return Json(new { data });
}
[HttpPost]
public IActionResult Update([FromBody] MaggotUpdateRequest request)
{
if (request == null)
{
return BadRequest(new { message = "Permintaan tidak valid." });
}
var updated = ReduksiSampahDataStore.UpdateReduksiValues(
Jenis,
request.Id,
request.Status,
request.B1,
request.B2,
request.B3,
request.B4,
request.B5,
request.B6,
request.B7,
request.B8,
request.B9,
request.B10,
request.B11,
request.B12);
if (!updated)
{
return NotFound(new { message = "Data maggot tidak ditemukan." });
}
return Ok(new { message = "Data maggot berhasil diperbarui." });
}
public class MaggotUpdateRequest
{
public int Id { get; set; }
public string? Status { get; set; }
public decimal? B1 { get; set; }
public decimal? B2 { get; set; }
public decimal? B3 { get; set; }
public decimal? B4 { get; set; }
public decimal? B5 { get; set; }
public decimal? B6 { get; set; }
public decimal? B7 { get; set; }
public decimal? B8 { get; set; }
public decimal? B9 { get; set; }
public decimal? B10 { get; set; }
public decimal? B11 { get; set; }
public decimal? B12 { get; set; }
}
}
}

View File

@ -16,7 +16,25 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Maggot",
BeratReduksi = 8,
BeratSampah = 15,
Alamat = AddressBlock("Tanah Abang", "Cideng", "002", "001")
Kecamatan = "Tanah Abang",
Kelurahan = "Cideng",
Rt = "002",
Rw = "001",
DetailAlamat = "Jl. Kebersihan No. 12",
Status = "Aktif",
B1 = 12.5m,
B2 = 13.2m,
B3 = 11.8m,
B4 = 14.4m,
B5 = 15.1m,
B6 = 12.9m,
B7 = 13.5m,
B8 = 12.3m,
B9 = 14.1m,
B10 = 13.7m,
B11 = 12.8m,
B12 = 13.0m,
Alamat = AddressBlock("Tanah Abang", "Cideng", "002", "001", "Jl. Kebersihan No. 12")
},
new()
{
@ -26,7 +44,25 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Maggot",
BeratReduksi = 10,
BeratSampah = 20,
Alamat = AddressBlock("Palmerah", "Kemanggisan", "003", "004")
Kecamatan = "Palmerah",
Kelurahan = "Kemanggisan",
Rt = "003",
Rw = "004",
DetailAlamat = "Jl. Kemanggisan Raya No. 9",
Status = "Aktif",
B1 = 10.2m,
B2 = 11.4m,
B3 = 10.8m,
B4 = 12.6m,
B5 = 13.3m,
B6 = 11.9m,
B7 = 12.8m,
B8 = 11.1m,
B9 = 12.4m,
B10 = 11.9m,
B11 = 11.4m,
B12 = 12.0m,
Alamat = AddressBlock("Palmerah", "Kemanggisan", "003", "004", "Jl. Kemanggisan Raya No. 9")
},
new()
{
@ -36,7 +72,25 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Maggot",
BeratReduksi = 7,
BeratSampah = 14,
Alamat = AddressBlock("Tebet", "Manggarai", "001", "002")
Kecamatan = "Tebet",
Kelurahan = "Manggarai",
Rt = "001",
Rw = "002",
DetailAlamat = "Jl. Manggarai Utara No. 7",
Status = "Tidak Aktif",
B1 = 9.5m,
B2 = 9.9m,
B3 = 10.1m,
B4 = 10.5m,
B5 = 10.2m,
B6 = 9.7m,
B7 = 9.9m,
B8 = 10.0m,
B9 = 9.8m,
B10 = 10.1m,
B11 = 9.6m,
B12 = 9.9m,
Alamat = AddressBlock("Tebet", "Manggarai", "001", "002", "Jl. Manggarai Utara No. 7")
},
new()
{
@ -46,7 +100,25 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Maggot",
BeratReduksi = 6,
BeratSampah = 12,
Alamat = AddressBlock("Cakung", "Jatinegara", "004", "006")
Kecamatan = "Cakung",
Kelurahan = "Jatinegara",
Rt = "004",
Rw = "006",
DetailAlamat = "Jl. Industri Hijau No. 5",
Status = "Aktif",
B1 = 14.6m,
B2 = 13.9m,
B3 = 14.1m,
B4 = 14.8m,
B5 = 15.3m,
B6 = 14.5m,
B7 = 15.0m,
B8 = 14.2m,
B9 = 15.1m,
B10 = 14.7m,
B11 = 14.3m,
B12 = 14.9m,
Alamat = AddressBlock("Cakung", "Jatinegara", "004", "006", "Jl. Industri Hijau No. 5")
},
new()
{
@ -56,7 +128,25 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Maggot",
BeratReduksi = 9,
BeratSampah = 16,
Alamat = AddressBlock("Koja", "Lagoa", "005", "003")
Kecamatan = "Koja",
Kelurahan = "Lagoa",
Rt = "005",
Rw = "003",
DetailAlamat = "Jl. Lagoa Mas No. 3",
Status = "Aktif",
B1 = 11.7m,
B2 = 11.3m,
B3 = 11.1m,
B4 = 11.8m,
B5 = 12.4m,
B6 = 11.5m,
B7 = 11.9m,
B8 = 11.2m,
B9 = 12.0m,
B10 = 11.6m,
B11 = 11.3m,
B12 = 11.9m,
Alamat = AddressBlock("Koja", "Lagoa", "005", "003", "Jl. Lagoa Mas No. 3")
},
new()
{
@ -66,7 +156,12 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Komposting",
BeratReduksi = 11,
BeratSampah = 18,
Alamat = AddressBlock("Beji", "Beji Timur", "006", "002")
Kecamatan = "Beji",
Kelurahan = "Beji Timur",
Rt = "006",
Rw = "002",
DetailAlamat = "Jl. Melati Hijau No. 10",
Alamat = AddressBlock("Beji", "Beji Timur", "006", "002", "Jl. Melati Hijau No. 10")
},
new()
{
@ -76,7 +171,12 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Komposting",
BeratReduksi = 13,
BeratSampah = 21,
Alamat = AddressBlock("Bekasi Timur", "Margahayu", "009", "005")
Kecamatan = "Bekasi Timur",
Kelurahan = "Margahayu",
Rt = "009",
Rw = "005",
DetailAlamat = "Jl. Margahayu Harmonis No. 8",
Alamat = AddressBlock("Bekasi Timur", "Margahayu", "009", "005", "Jl. Margahayu Harmonis No. 8")
},
new()
{
@ -86,7 +186,12 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Komposting",
BeratReduksi = 9,
BeratSampah = 15,
Alamat = AddressBlock("Bogor Utara", "Kedunghalang", "001", "004")
Kecamatan = "Bogor Utara",
Kelurahan = "Kedunghalang",
Rt = "001",
Rw = "004",
DetailAlamat = "Jl. Kedunghalang Sejahtera No. 2",
Alamat = AddressBlock("Bogor Utara", "Kedunghalang", "001", "004", "Jl. Kedunghalang Sejahtera No. 2")
},
new()
{
@ -96,7 +201,12 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Komposting",
BeratReduksi = 8,
BeratSampah = 13,
Alamat = AddressBlock("Karawaci", "Nusa Jaya", "008", "003")
Kecamatan = "Karawaci",
Kelurahan = "Nusa Jaya",
Rt = "008",
Rw = "003",
DetailAlamat = "Jl. Nusa Jaya Indah No. 11",
Alamat = AddressBlock("Karawaci", "Nusa Jaya", "008", "003", "Jl. Nusa Jaya Indah No. 11")
},
new()
{
@ -106,7 +216,12 @@ namespace BankSampahApp.Controllers.ReduksiSampah
JenisLokasi = "Komposting",
BeratReduksi = 12,
BeratSampah = 19,
Alamat = AddressBlock("Ciwandan", "Kebonsari", "010", "002")
Kecamatan = "Ciwandan",
Kelurahan = "Kebonsari",
Rt = "010",
Rw = "002",
DetailAlamat = "Jl. Kebonsari Permai No. 4",
Alamat = AddressBlock("Ciwandan", "Kebonsari", "010", "002", "Jl. Kebonsari Permai No. 4")
}
};
@ -133,6 +248,21 @@ namespace BankSampahApp.Controllers.ReduksiSampah
return item.PeriodeBulan.Contains(bulan.Trim(), StringComparison.OrdinalIgnoreCase);
}
public static bool MatchesPeriode(ReduksiSampahItem item, string? bulan, string? tahun)
{
if (!MatchesBulan(item, bulan))
{
return false;
}
if (string.IsNullOrWhiteSpace(tahun))
{
return true;
}
return item.PeriodeBulan.EndsWith(tahun.Trim(), StringComparison.OrdinalIgnoreCase);
}
public static object ToResponse(ReduksiSampahItem item) => new
{
id = item.Id,
@ -146,7 +276,78 @@ namespace BankSampahApp.Controllers.ReduksiSampah
aksi = ActionButtons
};
private static string AddressBlock(string kecamatan, string kelurahan, string rt, string rw)
public static object ToDetailedResponse(ReduksiSampahItem item) => new
{
id = item.Id,
periode = item.PeriodeBulan,
kecamatan = item.Kecamatan,
kelurahan = item.Kelurahan,
rt = item.Rt,
rw = item.Rw,
detail_alamat = item.DetailAlamat,
status = item.Status,
b1 = item.B1,
b2 = item.B2,
b3 = item.B3,
b4 = item.B4,
b5 = item.B5,
b6 = item.B6,
b7 = item.B7,
b8 = item.B8,
b9 = item.B9,
b10 = item.B10,
b11 = item.B11,
b12 = item.B12
};
public static bool UpdateReduksiValues(
string jenisLokasi,
int id,
string? status,
decimal? b1,
decimal? b2,
decimal? b3,
decimal? b4,
decimal? b5,
decimal? b6,
decimal? b7,
decimal? b8,
decimal? b9,
decimal? b10,
decimal? b11,
decimal? b12)
{
var item = Items.FirstOrDefault(x =>
x.Id == id &&
string.Equals(x.JenisLokasi, jenisLokasi, StringComparison.OrdinalIgnoreCase));
if (item == null)
{
return false;
}
if (!string.IsNullOrWhiteSpace(status))
{
item.Status = status;
}
if (b1.HasValue) item.B1 = b1.Value;
if (b2.HasValue) item.B2 = b2.Value;
if (b3.HasValue) item.B3 = b3.Value;
if (b4.HasValue) item.B4 = b4.Value;
if (b5.HasValue) item.B5 = b5.Value;
if (b6.HasValue) item.B6 = b6.Value;
if (b7.HasValue) item.B7 = b7.Value;
if (b8.HasValue) item.B8 = b8.Value;
if (b9.HasValue) item.B9 = b9.Value;
if (b10.HasValue) item.B10 = b10.Value;
if (b11.HasValue) item.B11 = b11.Value;
if (b12.HasValue) item.B12 = b12.Value;
return true;
}
private static string AddressBlock(string kecamatan, string kelurahan, string rt, string rw, string detailAlamat)
{
return $@"
<div class=""flex flex-col gap-1"">
@ -156,6 +357,7 @@ namespace BankSampahApp.Controllers.ReduksiSampah
<span>RT {rt}</span>
<span>RW {rw}</span>
</div>
<span class=""text-xs text-gray-500"">{detailAlamat}</span>
</div>";
}
@ -165,9 +367,27 @@ namespace BankSampahApp.Controllers.ReduksiSampah
public string PeriodeBulan { get; set; } = string.Empty;
public string NamaLokasi { get; set; } = string.Empty;
public string Alamat { get; set; } = string.Empty;
public string DetailAlamat { get; set; } = string.Empty;
public string Kecamatan { get; set; } = string.Empty;
public string Kelurahan { get; set; } = string.Empty;
public string Rt { get; set; } = string.Empty;
public string Rw { get; set; } = string.Empty;
public string JenisLokasi { get; set; } = string.Empty;
public string Status { get; set; } = "Aktif";
public double BeratReduksi { get; set; }
public double BeratSampah { get; set; }
public decimal B1 { get; set; }
public decimal B2 { get; set; }
public decimal B3 { get; set; }
public decimal B4 { get; set; }
public decimal B5 { get; set; }
public decimal B6 { get; set; }
public decimal B7 { get; set; }
public decimal B8 { get; set; }
public decimal B9 { get; set; }
public decimal B10 { get; set; }
public decimal B11 { get; set; }
public decimal B12 { get; set; }
}
}
}

View File

@ -1,253 +1,204 @@
@{
var jenis = "Komposting";
var tableUrl = Url.Content("~/ReduksiSampah/Komposting/Table");
ViewData["Title"] = $"Reduksi Sampah - {jenis}";
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">
<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 gap-2 items-center">
<select class="select rounded-full">
<option disabled selected>Pilih Tahun</option>
<option>2025</option>
<option>2024</option>
<option>2023</option>
<option>2022</option>
<option>2021</option>
<option>2020</option>
<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 type="button" class="btn btn-sm bg-bank-sampah-primary-500 rounded-full text-white"
onclick="modal_tambah.showModal()"><i class="ph ph-plus"></i>Tambah</button>
<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>
<!-- Modal History -->
<dialog id="modal_history" class="modal">
<div class="modal-box max-w-md">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="text-lg font-bold">History Data</h3>
<p class="py-4 text-base font-['Plus Jakarta Sans']">Terdapat 30 History perubahan data</p>
<div class="px-3 pt-4 pb-8 bg-gray-50">
<div class="flex flex-col gap-4 pl-4 border-l-2 border-gray-300">
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-primary-500 text-base">Current Version</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">24 Nov 2025, 12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">23 Nov 2025, 12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">22 Nov 2025, 12:26 pm</div>
</div>
</div>
</div>
</div>
</dialog>
<!-- End Modal History -->
<!-- Modal Validasi Data -->
<dialog id="modal_validasi" class="modal modal-bottom sm:modal-middle">
<div
class="modal-box w-full max-w-[591px] p-6 bg-white rounded-2xl inline-flex flex-col justify-center items-end gap-6">
<div class="self-stretch inline-flex justify-between items-center">
<div class="flex-1 justify-start text-gray-900 text-xl font-semibold font-['Plus Jakarta Sans'] leading-8">
Validasi Data</div>
<button type="button" class="w-5 h-5 relative" onclick="closeValidasiModal()">
<i class="ph ph-x text-gray-500 text-xl"></i>
</button>
</div>
<div class="self-stretch flex flex-col justify-center items-center">
<div class="self-stretch px-3 pt-3 pb-8 bg-gray-50 rounded-lg inline-flex justify-start items-start gap-4">
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Periode</div>
<div id="val_periode"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
29 Jul 2024</div>
</div>
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Nama Lokasi</div>
<div id="val_nama_lokasi"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
Jakarta Selatan</div>
</div>
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Jenis Lokasi</div>
<div id="val_jenis_lokasi"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
Maggot</div>
</div>
</div>
</div>
<div
class="self-stretch justify-start text-slate-800 text-base font-semibold font-['Plus Jakarta Sans'] leading-6">
Anda yakin ingin melakukan validasi data ?</div>
<div class="self-stretch inline-flex justify-end items-center gap-3">
<button type="button" class="btn btn-outline btn-sm rounded-full flex justify-center items-center gap-2"
onclick="confirmTolakValidasi()">
<div class="justify-start text-slate-800 text-sm font-semibold font-['Plus Jakarta Sans'] leading-5">
Tolak Validasi</div>
</button>
<button type="button" class="btn btn-success btn-sm rounded-full flex justify-center items-center gap-2"
onclick="confirmValidasi()">
<div class="justify-start text-white text-sm font-semibold font-['Plus Jakarta Sans'] leading-5">Ya
</div>
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- /modal validasi -->
<!-- Modal Tambah/Edit Data -->
<dialog id="modal_tambah" class="modal modal-bottom sm:modal-middle">
<div class="modal-box w-full max-w-2xl p-6 bg-white rounded-2xl">
<h3 id="modal_title" class="text-gray-900 text-xl font-semibold font-['Plus Jakarta Sans'] leading-8 mb-8">
Tambah Reduksi Sampah</h3>
<form id="formTambah" onsubmit="submitForm(event)">
<input type="hidden" id="edit_mode" value="false">
<input type="hidden" id="reduksi_sampah_id" value="">
<div class="flex flex-col gap-6">
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Jenis Lokasi
<span class="text-red-500">*</span>
</legend>
<select id="jenis_lokasi" class="select w-full" required>
<option value="Komposting">Komposting</option>
<option value="Maggot">Maggot</option>
</select>
</fieldset>
</div>
<div id="formDetail" class="flex flex-col gap-6">
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Lokasi<span class="text-red-500">*</span>
</legend>
<select id="lokasi" class="select w-full" required>
<option value="" disabled selected>Pilih salah satu</option>
</select>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Alamat
</legend>
<textarea id="alamat" rows="4" readonly class="textarea w-full"></textarea>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Bukti Kegiatan
<span class="text-red-500">*</span>
</legend>
<input type="file" class="file-input file w-full" />
<label class="label">Ukuran Maksimal 5MB</label>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Keterangan Kegiatan<span class="text-red-500">*</span>
</legend>
<textarea id="keterangan_kegiatan" rows="4" required placeholder="Keterangan Kegiatan"
class="textarea w-full"></textarea>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Tanggal Pelaksanaan Kegiatan
<span class="text-red-500">*</span>
</legend>
<input type="date" id="tgl_pelaksanaan_kegiatan" class="input w-full" required />
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Pelaksana Kegiatan<span class="text-red-500">*</span>
</legend>
<input type="text" id="preview" class="input w-full" placeholder="Pelaksana Kegiatan"
required />
</fieldset>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-3 justify-end">
<button type="button" class="btn btn-outline rounded-full w-full sm:w-auto" onclick="closeModal()">
Batal
</button>
<button type="submit"
class="btn bg-bank-sampah-primary-500 rounded-full text-white w-full sm:w-auto">
Simpan
</button>
</div>
</div>
</form>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- /modal tambah/edit -->
<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-zebra table w-full" id="example">
<table class="table w-full text-center" id="example">
<thead>
<tr>
<th>No</th>
<th>Periode</th>
<th>Nama Lokasi</th>
<th>Alamat</th>
<th>Jenis Lokasi</th>
<th>Berat Reduksi (Kg)</th>
<th>Berat Sampah (Kg)</th>
<th>Bukti Kegiatan</th>
<th>Aksi</th>
<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>
@ -257,166 +208,232 @@
</div>
</div>
@section Scripts {
<script type="text/javascript">
const jenisReduksi = '@jenis';
const tableAjaxUrl = '@tableUrl';
var table;
var allLokasi = [];
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 () {
$.ajax({
url: '/ReduksiSampah/Lokasi/Table',
method: 'GET',
success: function (response) {
allLokasi = (response.data || []).map(item => ({
jenis_lokasi: item.jenis_lokasi,
nama_lokasi: item.nama_lokasi,
kecamatan: item.kecamatan,
kelurahan: item.kelurahan,
detail_alamat: item.detail_alamat,
rt: item.rt,
rw: item.rw
}));
$('#jenis_lokasi').val(jenisReduksi).trigger('change');
$('#jenis_lokasi').prop('disabled', true);
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);
},
error: function () {
console.error("Gagal mengambil data lokasi dari /ReduksiSampah/Lokasi/Table");
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: tableAjaxUrl,
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');
},
columns: [
{ data: null, render: (d, t, r, m) => m.row + 1, orderable: false, searchable: false },
{ data: 'periode_bulan' },
{ data: 'nama_lokasi' },
{ data: 'alamat' },
{ data: 'jenis_lokasi' },
{ data: 'berat_reduksi' },
{ data: 'berat_sampah' },
{ data: 'gambar', orderable: false, searchable: false },
{ data: 'aksi', orderable: false, searchable: false },
]
});
$('#example').on('click', '.btn-history', function (e) {
e.preventDefault();
modal_history.showModal();
});
$('#example').on('click', '.btn-validation', function (e) {
e.preventDefault();
const row = table.row($(this).parents('tr')).data();
if (row) {
$('#val_periode').text(row.periode_bulan);
$('#val_nama_lokasi').text(row.nama_lokasi);
$('#val_jenis_lokasi').text(row.jenis_lokasi);
}
modal_validasi.showModal();
});
$('#jenis_lokasi').on('change', function () {
const jenisLokasi = $(this).val();
if (jenisLokasi) {
$('#formDetail').removeClass('hidden').hide().fadeIn(300);
populateNamaLokasiDropdown(jenisLokasi);
} else {
$('#formDetail').fadeOut(300);
}
});
$('#lokasi').on('change', function () {
const nama = $(this).val();
$('#example').on('click', '.btn-edit-row', function () {
const rowId = $(this).data('id');
setRowEditingState(rowId, true);
});
if (!nama) {
$('#alamat').val('');
$('#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;
}
const lokasi = allLokasi.find(item => item.nama_lokasi === nama);
$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 (lokasi) {
const formattedAlamat = `${lokasi.kecamatan}, ${lokasi.kelurahan}, RT ${lokasi.rt}, RW ${lokasi.rw}, ${lokasi.detail_alamat}`;
$('#alamat').val(formattedAlamat);
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);
});
$('#example').on('click', '.btn-edit', function (e) {
e.preventDefault();
return payload;
}
var row = table.row($(this).parents('tr')).data();
$('#edit_mode').val('true');
$('#reduksi_sampah_id').val(row.id);
$('#modal_title').text('Edit Reduksi Sampah');
modal_tambah.showModal();
});
$('#example').on('click', '.btn-delete', function (e) {
e.preventDefault();
const row = table.row($(this).parents('tr')).data();
function saveRowChanges(rowId) {
const payload = collectRowPayload(rowId);
if (!payload) {
Swal.fire({
title: 'Hapus data?',
text: `Data ${row?.nama_lokasi || ''} akan dihapus secara permanen.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire('Terhapus', 'Data berhasil dihapus (simulasi).', 'success');
table.ajax.reload();
icon: 'error',
title: 'Gagal',
text: 'Data tidak valid untuk disimpan.'
});
return;
}
});
});
});
function populateNamaLokasiDropdown(jenisLokasi) {
let options = '<option value=\"\">Pilih salah satu</option>';
if (jenisLokasi) {
const filtered = allLokasi.filter(item => item.jenis_lokasi === jenisLokasi);
filtered.forEach(item => {
options += `<option value="${item.nama_lokasi}">${item.nama_lokasi}</option>`;
$.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
});
}
$('#lokasi').html(options);
}
function closeModal() {
$('#formTambah')[0].reset();
$('#edit_mode').val('false');
$('#modal_title').text('Tambah Reduksi Sampah');
$('#jenis_lokasi').val(jenisReduksi);
modal_tambah.close();
}
function closeValidasiModal() {
modal_validasi.close();
}
function confirmValidasi() {
closeValidasiModal();
table.ajax.reload();
}
function confirmTolakValidasi() {
closeValidasiModal();
table.ajax.reload();
});
}
</script>
}

View File

@ -5,249 +5,200 @@ 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">
<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 gap-2 items-center">
<select class="select rounded-full">
<option disabled selected>Pilih Tahun</option>
<option>2025</option>
<option>2024</option>
<option>2023</option>
<option>2022</option>
<option>2021</option>
<option>2020</option>
<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 type="button" class="btn btn-sm bg-bank-sampah-primary-500 rounded-full text-white"
onclick="modal_tambah.showModal()"><i class="ph ph-plus"></i>Tambah</button>
<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>
<!-- Modal History -->
<dialog id="modal_history" class="modal">
<div class="modal-box max-w-md">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="text-lg font-bold">History Data</h3>
<p class="py-4 text-base font-['Plus Jakarta Sans']">Terdapat 30 History perubahan data</p>
<div class="px-3 pt-4 pb-8 bg-gray-50">
<div class="flex flex-col gap-4 pl-4 border-l-2 border-gray-300">
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-primary-500 text-base">Current Version</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">24 Nov 2025, 12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">23 Nov 2025, 12:26 pm</div>
</div>
<div class="flex flex-col gap-1">
<div class="text-gray-500 text-xs leading-4">Admin Subandri</div>
<div class="text-gray-600 text-base">22 Nov 2025, 12:26 pm</div>
</div>
</div>
</div>
</div>
</dialog>
<!-- End Modal History -->
<!-- Modal Validasi Data -->
<dialog id="modal_validasi" class="modal modal-bottom sm:modal-middle">
<div
class="modal-box w-full max-w-[591px] p-6 bg-white rounded-2xl inline-flex flex-col justify-center items-end gap-6">
<div class="self-stretch inline-flex justify-between items-center">
<div class="flex-1 justify-start text-gray-900 text-xl font-semibold font-['Plus Jakarta Sans'] leading-8">
Validasi Data</div>
<button type="button" class="w-5 h-5 relative" onclick="closeValidasiModal()">
<i class="ph ph-x text-gray-500 text-xl"></i>
</button>
</div>
<div class="self-stretch flex flex-col justify-center items-center">
<div class="self-stretch px-3 pt-3 pb-8 bg-gray-50 rounded-lg inline-flex justify-start items-start gap-4">
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Periode</div>
<div id="val_periode"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
29 Jul 2024</div>
</div>
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Nama Lokasi</div>
<div id="val_nama_lokasi"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
Jakarta Selatan</div>
</div>
<div class="flex-1 inline-flex flex-col justify-start items-start gap-1">
<div
class="self-stretch justify-start text-gray-500 text-xs font-normal font-['Plus Jakarta Sans'] leading-4">
Jenis Lokasi</div>
<div id="val_jenis_lokasi"
class="self-stretch justify-start text-slate-600 text-base font-normal font-['Plus Jakarta Sans'] leading-6">
Maggot</div>
</div>
</div>
</div>
<div
class="self-stretch justify-start text-slate-800 text-base font-semibold font-['Plus Jakarta Sans'] leading-6">
Anda yakin ingin melakukan validasi data ?</div>
<div class="self-stretch inline-flex justify-end items-center gap-3">
<button type="button" class="btn btn-outline btn-sm rounded-full flex justify-center items-center gap-2"
onclick="confirmTolakValidasi()">
<div class="justify-start text-slate-800 text-sm font-semibold font-['Plus Jakarta Sans'] leading-5">
Tolak Validasi</div>
</button>
<button type="button" class="btn btn-success btn-sm rounded-full flex justify-center items-center gap-2"
onclick="confirmValidasi()">
<div class="justify-start text-white text-sm font-semibold font-['Plus Jakarta Sans'] leading-5">Ya
</div>
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- /modal validasi -->
<!-- Modal Tambah/Edit Data -->
<dialog id="modal_tambah" class="modal modal-bottom sm:modal-middle">
<div class="modal-box w-full max-w-2xl p-6 bg-white rounded-2xl">
<h3 id="modal_title" class="text-gray-900 text-xl font-semibold font-['Plus Jakarta Sans'] leading-8 mb-8">
Tambah Reduksi Sampah</h3>
<form id="formTambah" onsubmit="submitForm(event)">
<input type="hidden" id="edit_mode" value="false">
<input type="hidden" id="reduksi_sampah_id" value="">
<div class="flex flex-col gap-6">
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Jenis Lokasi
<span class="text-red-500">*</span>
</legend>
<select id="jenis_lokasi" class="select w-full" required>
<option value="Komposting">Komposting</option>
<option value="Maggot">Maggot</option>
</select>
</fieldset>
</div>
<div id="formDetail" class="flex flex-col gap-6">
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Lokasi<span class="text-red-500">*</span>
</legend>
<select id="lokasi" class="select w-full" required>
<option value="" disabled selected>Pilih salah satu</option>
</select>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Alamat
</legend>
<textarea id="alamat" rows="4" readonly class="textarea w-full"></textarea>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Bukti Kegiatan
<span class="text-red-500">*</span>
</legend>
<input type="file" class="file-input file w-full" />
<label class="label">Ukuran Maksimal 5MB</label>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Keterangan Kegiatan<span class="text-red-500">*</span>
</legend>
<textarea id="keterangan_kegiatan" rows="4" required placeholder="Keterangan Kegiatan"
class="textarea w-full"></textarea>
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Tanggal Pelaksanaan Kegiatan
<span class="text-red-500">*</span>
</legend>
<input type="date" id="tgl_pelaksanaan_kegiatan" class="input w-full" required />
</fieldset>
</div>
<div class="flex flex-col">
<fieldset class="fieldset">
<legend class="fieldset-legend">
Pelaksana Kegiatan<span class="text-red-500">*</span>
</legend>
<input type="text" id="preview" class="input w-full" placeholder="Pelaksana Kegiatan"
required />
</fieldset>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-3 justify-end">
<button type="button" class="btn btn-outline rounded-full w-full sm:w-auto" onclick="closeModal()">
Batal
</button>
<button type="submit"
class="btn bg-bank-sampah-primary-500 rounded-full text-white w-full sm:w-auto">
Simpan
</button>
</div>
</div>
</form>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- /modal tambah/edit -->
<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 Maggot</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 Maggot</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-zebra table w-full" id="example">
<thead>
<table class="table w-full" id="example">
<thead class="text-black">
<tr>
<th>No</th>
<th>Periode</th>
<th>Nama Lokasi</th>
<th>Alamat</th>
<th>Jenis Lokasi</th>
<th>Berat Reduksi (Kg)</th>
<th>Berat Sampah (Kg)</th>
<th>Bukti Kegiatan</th>
<th>Aksi</th>
<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>
@ -257,166 +208,229 @@ ViewData["Title"] = $"Reduksi Sampah - {jenis}";
</div>
</div>
@section Scripts {
<script type="text/javascript">
const jenisReduksi = '@jenis';
const tableAjaxUrl = '@tableUrl';
var table;
var allLokasi = [];
const updateUrl = '@Url.Content("~/ReduksiSampah/Maggot/Update")';
const decimalFields = ['b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10', 'b11', 'b12'];
let table;
$(document).ready(function () {
$.ajax({
url: '/ReduksiSampah/Lokasi/Table',
method: 'GET',
success: function (response) {
allLokasi = (response.data || []).map(item => ({
jenis_lokasi: item.jenis_lokasi,
nama_lokasi: item.nama_lokasi,
kecamatan: item.kecamatan,
kelurahan: item.kelurahan,
detail_alamat: item.detail_alamat,
rt: item.rt,
rw: item.rw
}));
$('#jenis_lokasi').val(jenisReduksi).trigger('change');
$('#jenis_lokasi').prop('disabled', true);
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);
},
error: function () {
console.error("Gagal mengambil data lokasi dari /ReduksiSampah/Lokasi/Table");
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: tableAjaxUrl,
ajax: {
url: tableAjaxUrl,
data: function (d) {
d.tahun = $('#filter_tahun').val();
},
dataSrc: 'data',
error: function (xhr, status, err) {
console.error('Gagal memuat data maggot:', 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');
},
columns: [
{ data: null, render: (d, t, r, m) => m.row + 1, orderable: false, searchable: false },
{ data: 'periode_bulan' },
{ data: 'nama_lokasi' },
{ data: 'alamat' },
{ data: 'jenis_lokasi' },
{ data: 'berat_reduksi' },
{ data: 'berat_sampah' },
{ data: 'gambar', orderable: false, searchable: false },
{ data: 'aksi', orderable: false, searchable: false },
]
});
$('#example').on('click', '.btn-history', function (e) {
e.preventDefault();
modal_history.showModal();
});
$('#example').on('click', '.btn-validation', function (e) {
e.preventDefault();
const row = table.row($(this).parents('tr')).data();
if (row) {
$('#val_periode').text(row.periode_bulan);
$('#val_nama_lokasi').text(row.nama_lokasi);
$('#val_jenis_lokasi').text(row.jenis_lokasi);
}
modal_validasi.showModal();
});
$('#jenis_lokasi').on('change', function () {
const jenisLokasi = $(this).val();
if (jenisLokasi) {
$('#formDetail').removeClass('hidden').hide().fadeIn(300);
populateNamaLokasiDropdown(jenisLokasi);
} else {
$('#formDetail').fadeOut(300);
}
});
$('#lokasi').on('change', function () {
const nama = $(this).val();
$('#example').on('click', '.btn-edit-row', function () {
const rowId = $(this).data('id');
setRowEditingState(rowId, true);
});
if (!nama) {
$('#alamat').val('');
$('#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-sm select-warning text-black w-32"
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-sm input-warning 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;
}
const lokasi = allLokasi.find(item => item.nama_lokasi === nama);
$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 (lokasi) {
const formattedAlamat = `${lokasi.kecamatan}, ${lokasi.kelurahan}, RT ${lokasi.rt}, RW ${lokasi.rw}, ${lokasi.detail_alamat}`;
$('#alamat').val(formattedAlamat);
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);
});
$('#example').on('click', '.btn-edit', function (e) {
e.preventDefault();
return payload;
}
var row = table.row($(this).parents('tr')).data();
$('#edit_mode').val('true');
$('#reduksi_sampah_id').val(row.id);
$('#modal_title').text('Edit Reduksi Sampah');
modal_tambah.showModal();
});
$('#example').on('click', '.btn-delete', function (e) {
e.preventDefault();
const row = table.row($(this).parents('tr')).data();
function saveRowChanges(rowId) {
const payload = collectRowPayload(rowId);
if (!payload) {
Swal.fire({
title: 'Hapus data?',
text: `Data ${row?.nama_lokasi || ''} akan dihapus secara permanen.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire('Terhapus', 'Data berhasil dihapus (simulasi).', 'success');
table.ajax.reload();
icon: 'error',
title: 'Gagal',
text: 'Data tidak valid untuk disimpan.'
});
return;
}
});
});
});
function populateNamaLokasiDropdown(jenisLokasi) {
let options = '<option value=\"\">Pilih salah satu</option>';
if (jenisLokasi) {
const filtered = allLokasi.filter(item => item.jenis_lokasi === jenisLokasi);
filtered.forEach(item => {
options += `<option value="${item.nama_lokasi}">${item.nama_lokasi}</option>`;
$.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
});
}
$('#lokasi').html(options);
}
function closeModal() {
$('#formTambah')[0].reset();
$('#edit_mode').val('false');
$('#modal_title').text('Tambah Reduksi Sampah');
$('#jenis_lokasi').val(jenisReduksi);
modal_tambah.close();
}
function closeValidasiModal() {
modal_validasi.close();
}
function confirmValidasi() {
closeValidasiModal();
table.ajax.reload();
}
function confirmTolakValidasi() {
closeValidasiModal();
table.ajax.reload();
});
}
</script>
}

View File

@ -39,6 +39,7 @@
<!-- CSS -->
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="https://cdn.datatables.net/2.3.4/css/dataTables.tailwindcss.css" />
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/5.0.1/css/fixedColumns.dataTables.min.css" />
@await RenderSectionAsync("Styles", required: false)
<!-- Select2 -->
@ -69,6 +70,7 @@
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdn.datatables.net/2.3.4/js/dataTables.js"></script>
<script src="https://cdn.datatables.net/fixedcolumns/5.0.1/js/dataTables.fixedColumns.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="/plugins/datatables/dataTables.tailwindcss.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>

View File

@ -39,6 +39,7 @@
<!-- CSS -->
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="https://cdn.datatables.net/2.3.4/css/dataTables.tailwindcss.css" />
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/5.0.1/css/fixedColumns.dataTables.min.css" />
@await RenderSectionAsync("Styles", required: false)
<!-- Select2 -->
@ -63,6 +64,7 @@
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdn.datatables.net/2.3.4/js/dataTables.js"></script>
<script src="https://cdn.datatables.net/fixedcolumns/5.0.1/js/dataTables.fixedColumns.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="/plugins/datatables/dataTables.tailwindcss.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>