skl/resources/js/pages/admin/pelaporan/AL/index_AL copy.tsx

865 lines
37 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useEffect } from "react";
import AuthenticatedLayout from "@/layouts/authenticated-layout";
import { Head, router } from "@inertiajs/react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectValue,
} from "@/components/ui/select";
import {
Tooltip,
TooltipTrigger,
TooltipContent,
} from "@/components/ui/tooltip";
import {
ChevronLeft,
Printer,
FileDown,
CircleHelp,
Upload,
ArrowUpRight,
Save,
} from "lucide-react";
// import { Inertia } from "@inertiajs/inertia";
/* ----------------------------------
Contoh komponen modal upload
-------------------------------------*/
function ModalUploadUmum({
isOpen,
onClose,
onUpload,
rowCode,
title,
}: {
isOpen: boolean;
onClose: () => void;
onUpload: () => void;
rowCode: string;
title: string;
}) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div className="bg-white rounded-lg shadow-lg w-full max-w-xl">
<div className="p-4 border-b flex justify-between items-center">
<h2 className="font-semibold text-gray-800 text-lg">
Laporan Pengelolaan Air Limbah - {rowCode}. {title}
</h2>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
</button>
</div>
<div className="p-4">
<p className="text-sm text-gray-700 mb-3">
Silahkan unggah dokumen Anda dengan drag & drop atau
klik pada kotak ini. Ukuran file tidak boleh melebihi 20
MB.
</p>
<div className="border-2 border-dashed border-gray-300 rounded p-6 flex flex-col items-center justify-center text-gray-500">
<Upload className="w-10 h-10 mb-2" />
<span className="text-sm">
Drop file atau klik di sini
</span>
</div>
</div>
<div className="p-4 border-t flex justify-end">
<Button
onClick={() => {
onUpload();
onClose();
}}
>
Simpan
</Button>
</div>
</div>
</div>
);
}
/* ----------------------------------
Contoh komponen modal khusus A10
-------------------------------------*/
function ModalUploadA10({
isOpen,
onClose,
onUpload,
rowCode,
title,
}: {
isOpen: boolean;
onClose: () => void;
onUpload: () => void;
rowCode: string;
title: string;
}) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div className="bg-white rounded-lg shadow-lg w-full max-w-2xl">
<div className="p-4 border-b flex justify-between items-center">
<h2 className="font-semibold text-gray-800 text-lg">
Laporan Pengelolaan Air Limbah - {rowCode}. {title}
</h2>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
</button>
</div>
<div className="p-4 space-y-4">
<div className="border-2 border-dashed border-gray-300 rounded p-6 flex flex-col items-center justify-center text-gray-500">
<Upload className="w-10 h-10 mb-2" />
<span className="text-sm">
Drop file atau klik di sini
</span>
</div>
{/* Contoh form data sertifikat */}
<div className="grid grid-cols-2 gap-4">
<div>
<Label htmlFor="noSertifikat">
Nomor Sertifikat BNSP
</Label>
<input
id="noSertifikat"
type="text"
className="mt-1 w-full border-gray-300 rounded"
/>
</div>
<div>
<Label htmlFor="namaPemilik">
Nama Pemilik Sertifikat
</Label>
<input
id="namaPemilik"
type="text"
className="mt-1 w-full border-gray-300 rounded"
/>
</div>
<div>
<Label htmlFor="tanggalTerbit">
Tanggal Terbit
</Label>
<input
id="tanggalTerbit"
type="date"
className="mt-1 w-full border-gray-300 rounded"
/>
</div>
<div>
<Label htmlFor="masaBerlaku">
Masa Berlaku (tahun)
</Label>
<input
id="masaBerlaku"
type="number"
className="mt-1 w-full border-gray-300 rounded"
/>
</div>
<div>
<Label htmlFor="namaJabatan">Nama Jabatan</Label>
<input
id="namaJabatan"
type="text"
className="mt-1 w-full border-gray-300 rounded"
/>
</div>
</div>
{/* Tabel list sertifikat */}
<div className="mt-4">
<table className="w-full text-sm border border-gray-300">
<thead className="bg-gray-100">
<tr>
<th className="p-2 border border-gray-300">
Nomor Sertifikat
</th>
<th className="p-2 border border-gray-300">
Nama Pemilik
</th>
<th className="p-2 border border-gray-300">
Tanggal Terbit
</th>
<th className="p-2 border border-gray-300">
Masa Berlaku
</th>
<th className="p-2 border border-gray-300">
Nama Jabatan
</th>
</tr>
</thead>
<tbody>
{/* Contoh: kosong */}
<tr>
<td
colSpan={5}
className="p-2 text-center text-gray-500"
>
Tidak ada data di tabel
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="p-4 border-t flex justify-end">
<Button
onClick={() => {
onUpload();
onClose();
}}
>
Simpan
</Button>
</div>
</div>
</div>
);
}
/* ----------------------------------
Struktur data baris table
-------------------------------------*/
interface TableRow {
code: string; // A1, A2, ...
name: string; // Nama komponen
tooltip: string; // Keterangan tooltip
hasLampiran: boolean; // Apakah baris ini punya tombol upload?
lampiranLabel?: string; // Text di kolom lampiran ("Data...", "Lampiran...")
dropdownValue: "Ada" | "Tidak Ada"; // Default "Tidak Ada"
value: number; // 0 atau 100
isDisabled: boolean; // Dropdown disable?
verification?: string; // Kosong jika belum diverifikasi
}
/* ----------------------------------
Halaman utama
-------------------------------------*/
export default function PelaporanALForm() {
// Modal states
const [modalRowCode, setModalRowCode] = useState<string | null>(null); // untuk row umum
const [modalA10Open, setModalA10Open] = useState<boolean>(false); // khusus A10
// Data baris A1 - A5 (tidak ada lampiran)
const [rowsNoLampiran, setRowsNoLampiran] = useState<TableRow[]>([
{
code: "A1",
name: "Instalasi pengolah air limbah",
tooltip:
"Foto kondisi terkini instalasi pengolahan air limbah pada masa periode pelaporan (ditandai dengan cap waktu)",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
{
code: "A2",
name: "Flowmeter",
tooltip:
"Foto kondisi terkini flowmeter pada masa periode pelaporan (ditandai dengan cap waktu)",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
{
code: "A3",
name: "Titik pengambilan sampel",
tooltip:
"Foto kondisi terkini titik pengambilan sampel air limbah yang telah dilengkapi dengan penandaan dan koordinat pada masa periode pelaporan (ditandai dengan cap waktu)",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
{
code: "A4",
name: "Saluran air limbah & air hujan terpisah",
tooltip:
"Foto kondisi terkini saluran air limbah & air hujan yang terpisah pada masa periode pelaporan (ditandai dengan cap waktu)",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
{
code: "A5",
name: "Izin pembuangan air limbah",
tooltip:
"Izin pembuangan air limbah yang masih berlaku atau Persetujuan Teknis Pemenuhan Baku Mutu Air Limbah yang telah dimiliki",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
]);
// Data baris A6 - A10 (beberapa ada lampiran)
const [rowsWithLampiran, setRowsWithLampiran] = useState<TableRow[]>([
{
code: "A6",
name: "Pengujian air limbah",
tooltip:
"Data pengujian, parameter, dll. (nanti dijelaskan detail)",
hasLampiran: true,
lampiranLabel: "Data...",
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: true, // Baru aktif setelah upload
},
{
code: "A7",
name: "Pemenuhan baku mutu air limbah",
tooltip: "Nilai otomatis dari sistem. Tidak ada lampiran.",
hasLampiran: false,
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: false,
},
{
code: "A8",
name: "Pelaksanaan dan pemutusan wewenang",
tooltip:
"Hasil pencatatan swapantau harian parameter debit dan pH.",
hasLampiran: true,
lampiranLabel: "Lampiran...",
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: true,
},
{
code: "A9",
name: "Pembuatan neraca air",
tooltip:
"Neraca air bersih senyatanya selama periode pelaporan (3 bulan).",
hasLampiran: true,
lampiranLabel: "Lampiran...",
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: true,
},
{
code: "A10",
name: "Sertifikasi kompetensi",
tooltip:
"Sertifikat kompetensi personil (BNSP) yang masih berlaku, dsb.",
hasLampiran: true,
lampiranLabel: "Lampiran...",
dropdownValue: "Tidak Ada",
value: 0,
isDisabled: true,
},
]);
// Total I (IKLl) dari A1A10
const [totalIKLl, setTotalIKLl] = useState(0);
// Total II (SPBMAL) => mengikuti A6
const [totalSPBMAL, setTotalSPBMAL] = useState(0);
// Hitung total I setiap ada perubahan data
useEffect(() => {
const sumNoLampiran = rowsNoLampiran.reduce(
(acc, row) => acc + row.value,
0
);
const sumWithLampiran = rowsWithLampiran.reduce(
(acc, row) => acc + row.value,
0
);
const total = sumNoLampiran + sumWithLampiran; // total 10 komponen
const average = total / 10; // misalnya kita mau rata-ratakan
setTotalIKLl(average);
}, [rowsNoLampiran, rowsWithLampiran]);
// Hitung total II berdasarkan A6 (Jika "Ada" => 100, jika "Tidak Ada" => 0)
useEffect(() => {
const rowA6 = rowsWithLampiran.find((r) => r.code === "A6");
if (rowA6) {
setTotalSPBMAL(rowA6.dropdownValue === "Ada" ? 100 : 0);
}
}, [rowsWithLampiran]);
// Ubah dropdown value
const handleDropdownChange = (
code: string,
newVal: "Ada" | "Tidak Ada",
isNoLampiran: boolean
) => {
if (isNoLampiran) {
const updated = rowsNoLampiran.map((row) => {
if (row.code === code) {
return {
...row,
dropdownValue: newVal,
value: newVal === "Ada" ? 100 : 0,
};
}
return row;
});
setRowsNoLampiran(updated);
} else {
const updated = rowsWithLampiran.map((row) => {
if (row.code === code) {
return {
...row,
dropdownValue: newVal,
value: newVal === "Ada" ? 100 : 0,
};
}
return row;
});
setRowsWithLampiran(updated);
}
};
// Ketika user klik tombol upload di kolom lampiran
const handleOpenUploadModal = (row: TableRow) => {
// Jika baris A6 diklik, arahkan ke route "admin.pelaporan.indexIpal"
// atau path "/admin/pelaporan/al/ipal"
if (row.code === "A6") {
// Contoh menggunakan nama route inertia
// Inertia.visit(route("admin.pelaporan.indexIpal"));
// Atau langsung path
router.visit("/admin/pelaporan/al/ipal");
return;
}
// Jika A10, buka modal khusus
if (row.code === "A10") {
setModalA10Open(true);
} else {
// A8, A9 pakai modal umum
setModalRowCode(row.code);
}
};
// Setelah upload berhasil, dropdown diaktifkan
const handleUploadSuccess = (code: string) => {
setRowsWithLampiran((prev) =>
prev.map((row) => {
if (row.code === code) {
return {
...row,
isDisabled: false, // dropdown baru bisa dipilih
// lampiranLabel tidak berubah -> tetap "Data..." atau "Lampiran..."
};
}
return row;
})
);
};
// Render baris table tanpa lampiran (A1-A5, A7)
const renderRowNoLampiran = (row: TableRow) => {
return (
<tr key={row.code} className="hover:bg-gray-50">
<td className="border border-gray-300 p-2 font-medium">
{row.code}
</td>
<td className="border border-gray-300 p-2">
<div className="flex justify-between items-center w-full">
<div className="text-gray-800">{row.name}</div>
<Tooltip>
<TooltipTrigger>
<CircleHelp className="text-green-600 w-4 h-4 cursor-pointer" />
</TooltipTrigger>
<TooltipContent className="bg-white p-3 rounded-lg shadow-lg border max-w-sm whitespace-pre-line">
<h4 className="font-medium text-green-700 mb-1">
{row.name}
</h4>
<p className="text-sm text-gray-600">
{row.tooltip}
</p>
</TooltipContent>
</Tooltip>
</div>
</td>
{/* Hasil */}
<td className="border border-gray-300 p-2">
<Select
disabled={row.isDisabled}
onValueChange={(val: "Ada" | "Tidak Ada") =>
handleDropdownChange(row.code, val, true)
}
value={row.dropdownValue}
>
<SelectTrigger className="w-full border-gray-300 text-sm">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Ada">Ada</SelectItem>
<SelectItem value="Tidak Ada">Tidak Ada</SelectItem>
</SelectContent>
</Select>
</td>
<td className="border border-gray-300 p-2 text-center">
{row.value}
</td>
{/* Tidak ada kolom lampiran di sini */}
<td className="border border-gray-300 p-2">
{row.verification || ""}
</td>
<td className="border border-gray-300 p-2"></td>
</tr>
);
};
// Render baris table dengan lampiran (A6, A8, A9, A10)
const renderRowWithLampiran = (row: TableRow) => {
return (
<tr key={row.code} className="hover:bg-gray-50">
<td className="border border-gray-300 p-2 font-medium">
{row.code}
</td>
<td className="border border-gray-300 p-2">
<div className="flex justify-between items-center w-full">
<div className="text-gray-800">{row.name}</div>
<Tooltip>
<TooltipTrigger>
<CircleHelp className="text-green-600 w-4 h-4 cursor-pointer" />
</TooltipTrigger>
<TooltipContent className="bg-white p-3 rounded-lg shadow-lg border max-w-sm whitespace-pre-line">
<h4 className="font-medium text-green-700 mb-1">
{row.name}
</h4>
<p className="text-sm text-gray-600">
{row.tooltip}
</p>
</TooltipContent>
</Tooltip>
</div>
</td>
{/* Hasil */}
<td className="border border-gray-300 p-2">
<Select
disabled={row.isDisabled}
onValueChange={(val: "Ada" | "Tidak Ada") =>
handleDropdownChange(row.code, val, false)
}
value={row.dropdownValue}
>
<SelectTrigger className="w-full border-gray-300 text-sm">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Ada">Ada</SelectItem>
<SelectItem value="Tidak Ada">Tidak Ada</SelectItem>
</SelectContent>
</Select>
</td>
<td className="border border-gray-300 p-2 text-center">
{row.value}
</td>
{/* Lampiran */}
<td className="border border-gray-300 p-2">
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1 text-green-700 hover:bg-green-50 hover:text-green-800"
onClick={() => handleOpenUploadModal(row)}
>
<Upload className="h-4 w-4" />
{/* Teks tidak berubah setelah upload */}
<span>{row.lampiranLabel || "Data..."}</span>
</Button>
</td>
<td className="border border-gray-300 p-2">
{row.verification || ""}
</td>
<td className="border border-gray-300 p-2"></td>
</tr>
);
};
// Render
return (
<AuthenticatedLayout header="Pelaporan Air Limbah">
<Head title="Pelaporan Air Limbah" />
<div className="p-4 space-y-6">
<Card className="shadow-lg border-none">
<CardHeader className="bg-gradient-to-r from-green-800 to-green-700 text-white rounded-t-lg">
<div className="flex justify-between items-center">
<div>
<div className="flex items-center gap-2 mb-1">
<Badge className="bg-green-600">
Nama Perusahaan
</Badge>
</div>
<CardTitle className="text-xl font-bold">
Laporan Pengelolaan Air Limbah
</CardTitle>
<p className="text-sm opacity-90 mt-1">
Tahun 2025 - Periode Triwulan 1 (Januari -
Maret)
</p>
</div>
<div className="flex gap-2">
<Button
variant="secondary"
size="sm"
className="gap-1"
onClick={() => window.history.back()}
>
<ChevronLeft className="h-4 w-4" />
Kembali
</Button>
<Button
variant="secondary"
size="sm"
onClick={() => {
const style =
document.createElement("style");
style.innerHTML = `
@media print {
@page {
size: auto;
margin: 20mm;
}
body {
zoom: 0.5;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
}
`;
document.head.appendChild(style);
window.print();
document.head.removeChild(style);
}}
>
<Printer className="h-4 w-4" />
</Button>
<Button variant="secondary" size="sm">
<FileDown className="h-4 w-4" />
</Button>
</div>
</div>
</CardHeader>
<CardContent className="p-6">
{/* Main Table */}
<div className="border rounded-lg overflow-hidden shadow-sm">
<table className="w-full border-collapse border border-gray-300 text-sm">
<thead className="bg-gray-100">
<tr>
<th className="border border-gray-300 p-2 w-16">
No
</th>
<th className="border border-gray-300 p-2">
Komponen
</th>
<th className="border border-gray-300 p-2 w-32">
Hasil
</th>
<th className="border border-gray-300 p-2 w-24">
Nilai
</th>
{/* A1-A5 tidak punya lampiran, tapi kolom ini tetap ada
untuk A6, A8, A9, A10. Nanti baris A1-A5 isinya kosong. */}
<th className="border border-gray-300 p-2 w-32">
Lampiran
</th>
<th className="border border-gray-300 p-2 w-32">
Verifikasi
</th>
<th className="border border-gray-300 p-2 w-32">
Keterangan
</th>
</tr>
</thead>
<tbody>
{/* Baris I (Nilai IKLl) */}
<tr className="bg-green-50">
<td className="border border-gray-300 p-2">
I
</td>
<td className="border border-gray-300 p-2 font-medium">
Nilai tingkat ketaatan pengelolaan
air limbah (IKLl) (%)
</td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2">
<span className="font-semibold text-green-700">
{totalIKLl}
</span>
</td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2"></td>
</tr>
{/* Section 1: Persyaratan Teknis => A1-A4 */}
<tr className="bg-gray-50">
<td className="border border-gray-300 p-2"></td>
<td
className="border border-gray-300 p-2 font-semibold text-gray-700"
colSpan={5}
>
1. Persyaratan Teknis
</td>
<td className="border border-gray-300 p-2"></td>
</tr>
{rowsNoLampiran
.slice(0, 4)
.map((row) => renderRowNoLampiran(row))}
{/* Section 2: Perijinan => A5 */}
<tr className="bg-gray-50">
<td className="border border-gray-300 p-2"></td>
<td
className="border border-gray-300 p-2 font-semibold text-gray-700"
colSpan={5}
>
2. Perijinan
</td>
<td className="border border-gray-300 p-2"></td>
</tr>
{renderRowNoLampiran(rowsNoLampiran[4])}
{/* Section 3: Pelaksanaan => A6 - A10 */}
<tr className="bg-gray-50">
<td className="border border-gray-300 p-2"></td>
<td
className="border border-gray-300 p-2 font-semibold text-gray-700"
colSpan={5}
>
3. Pelaksanaan
</td>
<td className="border border-gray-300 p-2"></td>
</tr>
{rowsWithLampiran.map((row) => {
// A7 tidak punya lampiran => treat like no-lampiran row
if (row.code === "A7") {
return renderRowNoLampiran(row);
}
// A6, A8, A9, A10 => with lampiran
return renderRowWithLampiran(row);
})}
{/* Baris II (Nilai SPBMAL) */}
<tr className="bg-green-50">
<td className="border border-gray-300 p-2">
II
</td>
<td className="border border-gray-300 p-2 font-medium">
Nilai tingkat pemenuhan baku mutu
air limbah * (SPBMAL) (%)
</td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2">
<span className="font-semibold text-green-700">
{totalSPBMAL}
</span>
</td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2"></td>
<td className="border border-gray-300 p-2"></td>
</tr>
</tbody>
</table>
</div>
{/* Notes Section */}
<Card className="border shadow-sm mt-6">
<CardContent className="p-5">
<h2 className="text-lg font-semibold mb-3 text-gray-800 flex items-center gap-2">
<span>Catatan</span>
</h2>
<Textarea
placeholder="Tambahkan catatan atau komentar tambahan di sini..."
className="min-h-[100px] bg-white border-gray-300 focus:border-green-500 focus:ring-green-500"
/>
</CardContent>
</Card>
{/* Agreement & Submit Section */}
<div className="mt-8 space-y-6">
<div className="flex items-start gap-3 p-4 bg-green-50 rounded-lg border border-green-200">
<Checkbox
id="agreement"
className="mt-1 text-green-600 focus:ring-green-500"
/>
<div>
<Label
htmlFor="agreement"
className="text-sm font-medium text-gray-800"
>
Pernyataan Kebenaran Data
</Label>
<p className="text-sm leading-relaxed text-gray-700 mt-1">
Kami menyatakan bahwa laporan ini telah
disusun berdasarkan ketentuan peraturan
yang berlaku dan kami bersedia
bertanggung jawab atas kebenaran
data-data yang kami kirimkan sesuai
dengan fakta dilapangan.
</p>
</div>
</div>
<div className="flex justify-end gap-4">
<Button
variant="outline"
className="hover:bg-gray-100 gap-2"
>
<Save className="h-4 w-4" />
Simpan Draft
</Button>
<Button
className="bg-green-800 hover:bg-green-700 transition-colors px-8 gap-2"
onClick={handleSubmit}
>
<ArrowUpRight className="h-4 w-4" />
Kirim Laporan
</Button>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Modal Upload Umum (A6, A8, A9) */}
<ModalUploadUmum
isOpen={!!modalRowCode && modalRowCode !== "A10"}
onClose={() => setModalRowCode(null)}
onUpload={() => {
if (modalRowCode) {
handleUploadSuccess(modalRowCode);
}
}}
rowCode={modalRowCode || ""}
title={
rowsWithLampiran.find((r) => r.code === modalRowCode)
?.name || ""
}
/>
{/* Modal Upload A10 */}
<ModalUploadA10
isOpen={modalA10Open}
onClose={() => setModalA10Open(false)}
onUpload={() => {
// Setelah upload, dropdown diaktifkan
handleUploadSuccess("A10");
}}
rowCode="A10"
title="Sertifikasi kompetensi"
/>
</AuthenticatedLayout>
);
}