skl/resources/js/pages/admin/pelaporan/index_pelaporan.tsx

659 lines
31 KiB
TypeScript

import React, { useEffect, useState } from "react";
import AuthenticatedLayout from "@/layouts/authenticated-layout";
import { Head, Link } from "@inertiajs/react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Search, FileText, BadgeCheck } from "lucide-react";
import Select from "react-select";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
// -------------------------------------------------
// Tipe data (opsional, jika Anda pakai TypeScript)
interface Perusahaan {
PerusahaanId: number;
NamaPerusahaan: string;
}
interface PeriodePelaporan {
PeriodePelaporanId: number;
Nama: string; // misal: "Triwulan 1", "Semester 1", "Triwulan 2", dsb.
}
interface Pelaporan {
PelaporanId: number;
PerusahaanId: number;
PeriodePelaporanId: number;
Tahun: number;
// Contoh field
SKL?: number;
SPL?: number;
SKL_IL?: number;
SKL_AL?: number;
SKL_LB3?: number;
SKL_SB?: number;
SKL_BS?: number;
SKL_STB?: number;
SKL_LP?: number;
SKL_KDM?: number;
// relasi
perusahaan?: Perusahaan;
periodePelaporan?: PeriodePelaporan;
}
interface PelaporanIndexProps {
companies: Perusahaan[];
periodes: PeriodePelaporan[];
pelaporan: Pelaporan[];
}
// -------------------------------------------------
export default function PelaporanIndex({
companies,
periodes,
pelaporan,
}: PelaporanIndexProps) {
// -------------------------------------------------
// 1. Buat options dari data companies & periodes
const companyOptions = companies.map((c) => ({
value: c.PerusahaanId.toString(),
label: c.NamaPerusahaan,
}));
const periodeOptions = periodes.map((p) => ({
value: p.PeriodePelaporanId.toString(),
label: p.Nama,
}));
// -------------------------------------------------
// 2. State filter
const [year, setYear] = useState<string>("2025");
// Secara default, pilih perusahaan pertama & periode pertama (jika ada)
const [company, setCompany] = useState<{
value: string;
label: string;
} | null>(companyOptions.length > 0 ? companyOptions[0] : null);
const [selectedPeriode, setSelectedPeriode] = useState<{
value: string;
label: string;
} | null>(periodeOptions.length > 0 ? periodeOptions[0] : null);
// Data terfilter
const [filteredData, setFilteredData] = useState<Pelaporan[]>([]);
// -------------------------------------------------
// 3. Fungsi bantu: cari label periode, dsb.
function getPeriodeLabel(id: number) {
const found = periodes.find((p) => p.PeriodePelaporanId === id);
return found ? found.Nama : "-";
}
// Apakah “Triwulan 1/3” atau “Semester 1/3”?
// => cek label mengandung "1" atau "3"
function isOneOrThree(label: string) {
return label.includes("Triwulan 1") || label.includes("Triwulan 3");
}
// -------------------------------------------------
// 4. Fungsi warna & nilai
// - Jika data null => merah (belum diisi)
// - Jika data 0 => abu-abu
// - Jika data > 0 => hijau
// - Triwulan 1/3 => kolom IL, SB, BS, STB, KDM => blank
function getCellDisplay(
val: number | undefined,
hidden: boolean
): { text: string; style: string } {
if (hidden) {
// Kolom disembunyikan (kosong) => merah (belum diisi)
return { text: "", style: "bg-red-100 text-red-700" };
}
if (val === undefined || val === null) {
// Belum ada data => merah
return { text: "", style: "bg-red-100 text-red-700" };
}
if (val === 0) {
// Data 0 => abu-abu
return { text: "0.00", style: "bg-gray-100 text-gray-700" };
}
// Data > 0 => hijau
return { text: val.toFixed(2), style: "bg-green-100 text-green-700" };
}
// -------------------------------------------------
// 5. Fungsi Filter
const handleFilter = () => {
let temp = [...pelaporan];
// Filter Perusahaan
if (company) {
temp = temp.filter(
(item) => item.PerusahaanId.toString() === company.value
);
}
// Filter Tahun
if (year) {
temp = temp.filter((item) => item.Tahun.toString() === year);
}
// Filter Periode
if (selectedPeriode) {
temp = temp.filter(
(item) =>
item.PeriodePelaporanId.toString() === selectedPeriode.value
);
}
// Jika tidak ada data tapi filter sudah diisi => buat dummy row
if (
temp.length === 0 &&
company !== null &&
selectedPeriode !== null &&
year !== ""
) {
const dummy: Pelaporan = {
PelaporanId: 0, // menandakan dummy
PerusahaanId: parseInt(company.value, 10),
PeriodePelaporanId: parseInt(selectedPeriode.value, 10),
Tahun: parseInt(year, 10),
SKL: 0,
SPL: 0,
SKL_IL: 0,
SKL_AL: 0,
SKL_LB3: 0,
SKL_SB: 0,
SKL_BS: 0,
SKL_STB: 0,
SKL_LP: 0,
SKL_KDM: 0,
perusahaan: {
PerusahaanId: parseInt(company.value, 10),
NamaPerusahaan: company.label,
},
periodePelaporan: {
PeriodePelaporanId: parseInt(selectedPeriode.value, 10),
Nama: selectedPeriode.label,
},
};
temp = [dummy];
}
setFilteredData(temp);
};
// Agar default langsung memfilter
React.useEffect(() => {
handleFilter();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// -------------------------------------------------
// 6. Fungsi reset filter
const handleReset = () => {
setYear("2025");
setCompany(companyOptions.length > 0 ? companyOptions[0] : null);
setSelectedPeriode(
periodeOptions.length > 0 ? periodeOptions[0] : null
);
// Re-filter
setTimeout(() => handleFilter(), 0);
};
// -------------------------------------------------
return (
<AuthenticatedLayout header={"Pelaporan SKL"}>
<Head title="Pelaporan SKL" />
<div className="p-8">
{/* Filter Section */}
<div className="bg-white rounded-lg shadow-sm mb-6">
<Card className="shadow-md border-slate-200 justify-between">
<CardHeader className="pb-3">
<CardTitle className="text-lg font-medium">
Pelaporan SKL
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center w-full">
{/* Select Perusahaan */}
<Select
options={companyOptions}
value={company}
onChange={setCompany}
placeholder="Pilih Perusahaan"
isSearchable
className="w-full sm:w-[300px]"
/>
<div className="flex gap-4 w-full sm:w-auto">
<Input
type="number"
value={year}
onChange={(e) =>
setYear(e.target.value)
}
className="w-full sm:w-24"
/>
<Select
options={periodeOptions}
value={selectedPeriode}
onChange={setSelectedPeriode}
placeholder="Pilih Periode"
className="w-full sm:w-[150px]"
/>
</div>
<Button
className="w-full sm:w-auto"
onClick={handleFilter}
>
<Search className="w-4 h-4 mr-2" />
Cari
</Button>
<Button
variant="outline"
className="w-full sm:w-auto"
onClick={handleReset}
>
Reset
</Button>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Table Section */}
<div className="w-full overflow-x-auto">
<Table className="min-w-[1000px] border-collapse">
<TableHeader>
<TableRow className="border-b border-t bg-green-800">
<TableHead
rowSpan={2}
className="w-[50px] border-r border-l text-white"
>
No.
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
Nama Perusahaan
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
Tahun
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
Periode Pelaporan
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
SKL (%)
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
SPBM (%)
</TableHead>
<TableHead
colSpan={8}
className="border-r text-center text-white"
>
Hasil (Nilai SKL)
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
STT
</TableHead>
<TableHead
rowSpan={2}
className="border-r text-center text-white"
>
SE
</TableHead>
</TableRow>
<TableRow className="border-b bg-green-600">
<TableHead className="text-center border-r text-white">
IL
</TableHead>
<TableHead className="text-center border-r text-white">
AL
</TableHead>
<TableHead className="text-center border-r text-white">
LB3
</TableHead>
<TableHead className="text-center border-r text-white">
SB
</TableHead>
<TableHead className="text-center border-r text-white">
BS
</TableHead>
<TableHead className="text-center border-r text-white">
STB
</TableHead>
<TableHead className="text-center border-r text-white">
LP
</TableHead>
<TableHead className="text-center border-r text-white">
KDM
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="border-b">
{filteredData.length > 0 ? (
filteredData.map((item, index) => {
// Ambil label periode
const labelPeriode = getPeriodeLabel(
item?.PeriodePelaporanId
);
// Apakah triwulan/semester 1/3?
const hidden = isOneOrThree(labelPeriode);
// Dapatkan style + text untuk IL
const IL = getCellDisplay(
item?.SKL_IL,
hidden
);
const AL = getCellDisplay(
item.SKL_AL,
false
); // AL selalu tampil
const LB3 = getCellDisplay(
item.SKL_LB3,
false
);
const SB = getCellDisplay(
item.SKL_SB,
hidden
);
const BS = getCellDisplay(
item.SKL_BS,
hidden
);
const STB = getCellDisplay(
item.SKL_STB,
hidden
);
const LP = getCellDisplay(
item.SKL_LP,
false
);
const KDM = getCellDisplay(
item.SKL_KDM,
hidden
);
// Dapatkan style + text untuk SKL, SPL
const skl = getCellDisplay(item.SKL, false);
const spl = getCellDisplay(item.SPL, false);
return (
<TableRow
key={item.PelaporanId}
className="border-b"
>
<TableCell className="text-center border-r hover:underline border-l">
{index + 1}
</TableCell>
<TableCell className="text-center border-r hover:underline">
{item.perusahaan
?.NamaPerusahaan ?? "-"}
</TableCell>
<TableCell className="text-center border-r hover:underline">
{item.Tahun}
</TableCell>
<TableCell className="text-center border-r hover:underline">
{labelPeriode}
</TableCell>
{/* SKL & SPBM */}
<TableCell
className={`text-center border-r hover:underline ${skl.style}`}
>
{skl.text}
</TableCell>
<TableCell
className={`text-center border-r hover:underline ${spl.style}`}
>
{spl.text}
</TableCell>
{/* IL */}
<TableCell
className={`text-center border-r hover:underline ${IL.style}`}
>
{IL.text}
</TableCell>
{/* AL */}
<TableCell
className={`text-center border-r hover:underline ${AL.style}`}
>
{item.PerusahaanId > 0 ? (
<Link
href={route(
"admin.pelaporanAL.index",
{
id: btoa(
`perusahaan-${item.PerusahaanId}`
),
}
)}
>
{AL.text}
</Link>
) : (
AL.text
)}
</TableCell>
{/* LB3 */}
<TableCell
className={`text-center border-r hover:underline ${LB3.style}`}
>
{LB3.text}
</TableCell>
{/* SB */}
<TableCell
className={`text-center border-r hover:underline ${SB.style}`}
>
{SB.text}
</TableCell>
{/* BS */}
<TableCell
className={`text-center border-r hover:underline ${BS.style}`}
>
{BS.text}
</TableCell>
{/* STB */}
<TableCell
className={`text-center border-r hover:underline ${STB.style}`}
>
{STB.text}
</TableCell>
{/* LP */}
<TableCell
className={`text-center border-r hover:underline ${LP.style}`}
>
{LP.text}
</TableCell>
{/* KDM */}
<TableCell
className={`text-center border-r hover:underline ${KDM.style}`}
>
{KDM.text}
</TableCell>
{/* STT */}
<TableCell className="text-center border-r">
<FileText className="w-4 h-4" />
</TableCell>
{/* SE */}
<TableCell className="text-center border-r">
<FileText className="w-4 h-4" />
</TableCell>
</TableRow>
);
})
) : (
<TableRow>
<TableCell
colSpan={16}
className="text-center py-4"
>
Tidak ada data
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{/* Legend Section */}
<div className="mt-6 flex flex-col md:grid md:grid-cols-2 gap-6">
{/* Keterangan Section */}
<div className="bg-white rounded-lg shadow-sm border p-4 md:p-6">
<div className="flex items-center gap-2 mb-4">
<FileText className="w-5 h-5 text-green-600" />
<h3 className="font-semibold text-lg">
Keterangan
</h3>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
SKL : Status Ketaatan Lingkungan
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
SPBM : Status Pemenuhan Baku Mutu
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
IL : Ijin Lingkungan
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">AL : Air Limbah</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">LB3 : Limbah B3</p>
</div>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
SB : Sumber Bergerak
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
BS : Kebisingan & Udara Ambien
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
STB : Sumber Tidak Bergerak
</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">LP : Limbah Padat</p>
</div>
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
KDM : Kawasan Dilarang Merokok
</p>
</div>
</div>
<div className="space-y-2 pt-3">
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
STT : Surat Tanda Terima
</p>
</div>
</div>
<div className="space-y-2 pt-3">
<div className="flex items-center gap-2">
<div className="min-w-[8px] w-2 h-2 rounded-full bg-green-500"></div>
<p className="text-sm">
SE : Surat Evaluasi
</p>
</div>
</div>
</div>
</div>
{/* Status Data Section */}
<div className="bg-white rounded-lg shadow-sm border p-4 md:p-6 mt-4 md:mt-0">
<div className="flex items-center gap-2 mb-4">
<BadgeCheck className="w-5 h-5 text-green-600" />
<h3 className="font-semibold text-lg">
Status Data
</h3>
</div>
<div className="space-y-3">
<div className="flex items-center gap-3 p-2 rounded-md bg-red-100">
<div className="min-w-[12px] w-3 h-3 rounded-full bg-red-500"></div>
<p className="text-sm text-red-700">
Data belum diisi
</p>
</div>
<div className="flex items-center gap-3 p-2 rounded-md bg-gray-100">
<div className="min-w-[12px] w-3 h-3 rounded-full bg-gray-500"></div>
<p className="text-sm text-gray-700">
Data telah diisi dan belum diverifikasi
</p>
</div>
<div className="flex items-center gap-3 p-2 rounded-md bg-orange-100">
<div className="min-w-[12px] w-3 h-3 rounded-full bg-orange-500"></div>
<p className="text-sm text-orange-700">
Data telah siap untuk diverifikasi
</p>
</div>
<div className="flex items-center gap-3 p-2 rounded-md bg-green-100">
<div className="min-w-[12px] w-3 h-3 rounded-full bg-green-500"></div>
<p className="text-sm text-green-700">
Data telah diisi dan diverifikasi
</p>
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}