Done for now

This commit is contained in:
Md Afiq Iskandar 2024-09-05 10:54:53 +08:00
parent 7b1e5bbc13
commit efdd28afbd
28 changed files with 1438 additions and 655 deletions

View File

@ -1,4 +1,34 @@
export default [ export default [
{
header: "Forensik",
description: "",
child: [
{
title: "FOR-01",
icon: "ph:number-circle-one-fill",
child: [
{
title: "Permohonan Temujanji",
path: "/permohonan-temujanji/senarai",
child: [],
meta: {},
},
{
title: "Kaunter Semakan ",
path: "/kemaskini-daftar/senarai",
child: [],
meta: {},
},
// {
// title: "Kemaskini Daftar",
// path: "/kemaskini-daftar/senarai",
// child: [],
// meta: {},
// },
],
},
],
},
{ {
header: "Utama", header: "Utama",
description: "", description: "",
@ -122,36 +152,7 @@ export default [
], ],
meta: {}, meta: {},
}, },
{
header: "Prototaip Forensik",
description: "",
child: [
{
title: "FOR-01",
icon: "ph:number-circle-one-fill",
child: [
{
title: "Permohonan Temujanji",
path: "/permohonan-temujanji/senarai",
child: [],
meta: {},
},
{
title: "Kaunter Semakan ",
path: "/kemaskini-daftar/senarai",
child: [],
meta: {},
},
// {
// title: "Kemaskini Daftar",
// path: "/kemaskini-daftar/senarai",
// child: [],
// meta: {},
// },
],
},
],
},
{ {
header: "Pengurusan", header: "Pengurusan",
description: "", description: "",

View File

@ -159,7 +159,7 @@ const showTooltip = (codeId, message) => {
}" }"
:options-advanced="{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false, filterable: false,
}" }"
advanced advanced
@ -217,7 +217,7 @@ const showTooltip = (codeId, message) => {
}" }"
:options-advanced="{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false, filterable: false,
}" }"
advanced advanced

View File

@ -46,7 +46,6 @@
label="Ringkasan Kenyataan Kes" label="Ringkasan Kenyataan Kes"
v-model="ringkasanKenyataanKes" v-model="ringkasanKenyataanKes"
validation="required" validation="required"
disabled
/> />
<FormKit <FormKit
type="number" type="number"
@ -55,72 +54,56 @@
validation="required|number" validation="required|number"
disabled disabled
/> />
<FormKit
type="text" <!-- Barang Section -->
label="Jenis Barang" <div class="mb-4">
v-model="jenisBarang" <h3 class="mb-2">Senarai Barang</h3>
validation="required" <table
disabled v-if="barangList.length > 0"
/> class="w-full border-collapse border border-gray-300 mb-2"
<FormKit >
type="text" <thead>
label="Tanda Barang" <tr class="bg-gray-100">
v-model="tandaBarang" <th class="border border-gray-300 p-2">Jenis Barang</th>
validation="required" <th class="border border-gray-300 p-2">Kuantiti</th>
disabled </tr>
/> </thead>
<FormKit <tbody>
type="text" <tr v-for="(barang, index) in barangList" :key="index">
label="Keadaan Barang" <td class="border border-gray-300 p-2">
v-model="keadaanBarang" {{
validation="required" barang.jenisBarangDetailLabel
disabled ? barang.jenisBarangDetailLabel
/> : barang.jenisBarangDetail
<FormKit }}
type="number" </td>
label="Kuantiti Barang" <td class="border border-gray-300 p-2">
v-model="kuantitiBarang" {{ barang.kuantitiBarang }}
validation="required|number" </td>
disabled </tr>
/> </tbody>
<FormKit </table>
type="select" <div v-else class="text-gray-500 mb-2">Tiada barang ditambah</div>
label="Jenis Barang" </div>
v-model="jenisBarangDetail"
:options="jenisBarangDetailOptions"
validation="required"
disabled
/>
<FormKit
type="select"
label="Jenis Barang Siber"
v-model="jenisBarangSiber"
:options="jenisBarangSiberOptions"
validation="required"
disabled
/>
<FormKit <FormKit
type="text" type="text"
label="No Kertas Siasatan" label="No Kertas Siasatan"
v-model="noKertasSiasatan" v-model="noKertasSiasatan"
disabled
/> />
<FormKit <FormKit
type="text" type="text"
label="No Laporan Polis" label="No Laporan Polis"
v-model="noLaporanPolis" v-model="noLaporanPolis"
disabled
/> />
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<rs-button type="button" @click="navi" variant="danger" <rs-button type="button" @click="navigateBack" variant="danger"
>Kembali</rs-button >Kembali</rs-button
> >
<rs-button type="button" @click="confirmBatal" variant="primary" <!-- <rs-button type="button" @click="confirmBatal" variant="primary"
>Tolak</rs-button >Tolak</rs-button
> > -->
<rs-button type="submit" @click="confirmSah" variant="success" <rs-button btn-type="submit" variant="success">Hantar</rs-button>
>Sah</rs-button
>
</div> </div>
</FormKit> </FormKit>
</rs-card> </rs-card>
@ -128,11 +111,9 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const { $swal } = useNuxtApp();
const noSiri = ref(route.params.noSiri); const noSiri = ref(route.params.noSiri);
@ -143,26 +124,11 @@ const namaPenghantar = ref("");
const pangkatPenghantar = ref(""); const pangkatPenghantar = ref("");
const ringkasanKenyataanKes = ref(""); const ringkasanKenyataanKes = ref("");
const bilangan = ref(0); const bilangan = ref(0);
const jenisBarang = ref(""); const barangList = ref([]);
const tandaBarang = ref("");
const keadaanBarang = ref("");
const kuantitiBarang = ref(0);
const jenisBarangDetail = ref("");
const jenisBarangSiber = ref("");
const noKertasSiasatan = ref(""); const noKertasSiasatan = ref("");
const noLaporanPolis = ref(""); const noLaporanPolis = ref("");
const jenisBarangDetailOptions = [ const jenisBarangDetailOptions = ref([]);
"PASPORT",
"MALPASS",
"CAP KESELAMATAN",
"CAP JARI",
"PEMERIKSAAN",
"I-KAD",
"LAIN-LAIN",
];
const jenisBarangSiberOptions = ["SIBER", "TULISAN TANGAN"];
const navigateBack = () => { const navigateBack = () => {
router.back(); router.back();
@ -177,12 +143,6 @@ const isFormValid = () => {
pangkatPenghantar, pangkatPenghantar,
ringkasanKenyataanKes, ringkasanKenyataanKes,
bilangan, bilangan,
jenisBarang,
tandaBarang,
keadaanBarang,
kuantitiBarang,
jenisBarangDetail,
jenisBarangSiber,
noKertasSiasatan, noKertasSiasatan,
noLaporanPolis, noLaporanPolis,
]; ];
@ -192,35 +152,38 @@ const isFormValid = () => {
); );
}; };
const simpan = () => { const submitForm = async () => {
if (isFormValid()) { if (isFormValid()) {
console.log("Form data saved"); try {
} else { const response = await $fetch(`/api/kaunter-permohonan/${noSiri.value}`, {
console.error("Please fill in all required fields."); method: "PUT",
} body: {
};
const submitForm = () => {
if (isFormValid()) {
console.log({
namaPemohon: namaPemohon.value,
pangkatPemohon: pangkatPemohon.value,
noPegawaiPemohon: noPegawaiPemohon.value,
namaPenghantar: namaPenghantar.value,
pangkatPenghantar: pangkatPenghantar.value,
ringkasanKenyataanKes: ringkasanKenyataanKes.value, ringkasanKenyataanKes: ringkasanKenyataanKes.value,
bilangan: bilangan.value,
jenisBarang: jenisBarang.value,
tandaBarang: tandaBarang.value,
keadaanBarang: keadaanBarang.value,
kuantitiBarang: kuantitiBarang.value,
jenisBarangDetail: jenisBarangDetail.value,
jenisBarangSiber: jenisBarangSiber.value,
noKertasSiasatan: noKertasSiasatan.value, noKertasSiasatan: noKertasSiasatan.value,
noLaporanPolis: noLaporanPolis.value, noLaporanPolis: noLaporanPolis.value,
},
}); });
navigateTo(`/kemaskini-daftar/kemaskini/sah/${noSiri.value}`); if (response.statusCode === 200) {
await $swal.fire({
title: "Berjaya!",
text: "Permohonan telah berjaya dikemaskini.",
icon: "success",
confirmButtonText: "OK",
});
router.push("/kemaskini-daftar/senarai");
} else {
throw new Error(response.message);
}
} catch (error) {
$swal.fire({
title: "Ralat!",
text: error.message || "Gagal mengemaskini permohonan. Sila cuba lagi.",
icon: "error",
confirmButtonText: "OK",
});
}
} else { } else {
$swal.fire({ $swal.fire({
title: "Ralat!", title: "Ralat!",
@ -260,75 +223,64 @@ const confirmSah = () => {
}) })
.then((result) => { .then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
submitForm();
} }
}); });
}; };
function generateSampleData(noSiri) { const fetchLookupData = async (type) => {
const randomNumber = (min, max) => try {
Math.floor(Math.random() * (max - min + 1) + min); const response = await $fetch(`/api/lookup?type=${type}`);
const randomChoice = (arr) => arr[Math.floor(Math.random() * arr.length)]; if (response.statusCode === 200) {
return response.data;
const namaPemohon = [ }
"Ali bin Abu", } catch (error) {
"Siti binti Ahmad", console.error(`Error fetching ${type} lookup data:`, error);
"Muthu a/l Rajan", return [];
"Lim Wei Ling", }
];
const pangkat = ["Inspektor", "Sarjan", "Koperal", "Konstabel"];
const jenisBarangOptions = [
"Dokumen",
"Peralatan Elektronik",
"Senjata",
"Dadah",
"Barang Kemas",
];
const keadaanBarangOptions = ["Baik", "Sederhana", "Rosak"];
return {
noSiri: noSiri,
namaPemohon: randomChoice(namaPemohon),
pangkatPemohon: randomChoice(pangkat),
noPegawaiPemohon: `PG${randomNumber(10000, 99999)}`,
namaPenghantar: randomChoice(namaPemohon),
pangkatPenghantar: randomChoice(pangkat),
ringkasanKenyataanKes: `Kes ${noSiri}: Penemuan barang bukti dalam serbuan di lokasi ${randomChoice(
["A", "B", "C", "D"]
)}`,
bilangan: randomNumber(1, 10),
jenisBarang: randomChoice(jenisBarangOptions),
tandaBarang: `TB-${randomNumber(1000, 9999)}`,
keadaanBarang: randomChoice(keadaanBarangOptions),
kuantitiBarang: randomNumber(1, 100),
jenisBarangDetail: randomChoice(jenisBarangDetailOptions),
jenisBarangSiber: randomChoice(jenisBarangSiberOptions),
noKertasSiasatan: `KS-${randomNumber(10000, 99999)}`,
noLaporanPolis: `RPT-${randomNumber(100000, 999999)}`,
}; };
const fetchExistingData = async (noSiri) => {
try {
const response = await $fetch(`/api/permohonan/${noSiri}`);
if (response.statusCode === 200) {
return response.data;
}
} catch (error) {
console.error("Error fetching existing data:", error);
$swal.fire({
title: "Ralat!",
text: "Gagal mendapatkan data permohonan.",
icon: "error",
confirmButtonText: "OK",
});
}
};
onMounted(async () => {
const existingData = await fetchExistingData(noSiri.value);
if (existingData) {
namaPemohon.value = existingData.namaPemohon;
pangkatPemohon.value = existingData.pangkatPemohon;
noPegawaiPemohon.value = existingData.noPegawaiPemohon;
namaPenghantar.value = existingData.namaPenghantar;
pangkatPenghantar.value = existingData.pangkatPenghantar;
ringkasanKenyataanKes.value = existingData.ringkasanKenyataanKes;
bilangan.value = existingData.bilangan;
barangList.value = existingData.barangList;
noKertasSiasatan.value = existingData.noKertasSiasatan;
noLaporanPolis.value = existingData.noLaporanPolis;
} }
onMounted(() => { jenisBarangDetailOptions.value = await fetchLookupData("jenis_barang");
const sampleData = generateSampleData(noSiri.value);
namaPemohon.value = sampleData.namaPemohon;
pangkatPemohon.value = sampleData.pangkatPemohon;
noPegawaiPemohon.value = sampleData.noPegawaiPemohon;
namaPenghantar.value = sampleData.namaPenghantar;
pangkatPenghantar.value = sampleData.pangkatPenghantar;
ringkasanKenyataanKes.value = sampleData.ringkasanKenyataanKes;
bilangan.value = sampleData.bilangan;
jenisBarang.value = sampleData.jenisBarang;
tandaBarang.value = sampleData.tandaBarang;
keadaanBarang.value = sampleData.keadaanBarang;
kuantitiBarang.value = sampleData.kuantitiBarang;
jenisBarangDetail.value = sampleData.jenisBarangDetail;
jenisBarangSiber.value = sampleData.jenisBarangSiber;
noKertasSiasatan.value = sampleData.noKertasSiasatan;
noLaporanPolis.value = sampleData.noLaporanPolis;
}); });
const { $swal } = useNuxtApp(); const getJenisBarangLabel = (value) => {
const option = jenisBarangDetailOptions.value.find(
(opt) => opt.__original === value
);
return option ? option.label : value;
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -62,7 +62,7 @@
<div class="space-y-4"> <div class="space-y-4">
<h2 class="text-xl font-semibold">Butiran Pegawai</h2> <h2 class="text-xl font-semibold">Butiran Pegawai</h2>
<div <div
v-for="role in ['PENYIASAT', 'PENGHANTAR', 'PENERIMA', 'PEMERIKSA']" v-for="role in ['PENYIASAT', 'PENGHANTAR', 'PEMERIKSA', 'PENERIMA']"
:key="role" :key="role"
class="grid grid-cols-1 md:grid-cols-3 gap-4" class="grid grid-cols-1 md:grid-cols-3 gap-4"
> >
@ -71,8 +71,6 @@
:name="`pegawai.${role}.nama`" :name="`pegawai.${role}.nama`"
:label="`${role} - Nama`" :label="`${role} - Nama`"
v-model="generatedData.pegawai[role].nama" v-model="generatedData.pegawai[role].nama"
validation="required"
:validation-messages="{ required: 'Nama diperlukan' }"
disabled disabled
/> />
<FormKit <FormKit
@ -80,8 +78,6 @@
:name="`pegawai.${role}.pangkat`" :name="`pegawai.${role}.pangkat`"
:label="`${role} - Pangkat`" :label="`${role} - Pangkat`"
v-model="generatedData.pegawai[role].pangkat" v-model="generatedData.pegawai[role].pangkat"
validation="required"
:validation-messages="{ required: 'Pangkat diperlukan' }"
disabled disabled
/> />
<FormKit <FormKit
@ -89,8 +85,6 @@
:name="`pegawai.${role}.noPegawai`" :name="`pegawai.${role}.noPegawai`"
:label="`${role} - No Pegawai`" :label="`${role} - No Pegawai`"
v-model="generatedData.pegawai[role].noPegawai" v-model="generatedData.pegawai[role].noPegawai"
validation="required"
:validation-messages="{ required: 'No Pegawai diperlukan' }"
disabled disabled
/> />
</div> </div>
@ -99,6 +93,7 @@
<!-- Peralatan and Langkah2 --> <!-- Peralatan and Langkah2 -->
<div class="space-y-4"> <div class="space-y-4">
<FormKit <FormKit
v-model="generatedData.peralatan"
type="textarea" type="textarea"
name="peralatan" name="peralatan"
label="Peralatan" label="Peralatan"
@ -107,6 +102,7 @@
:rows="3" :rows="3"
/> />
<FormKit <FormKit
v-model="generatedData.langkah2"
type="textarea" type="textarea"
name="langkah2" name="langkah2"
label="Langkah-langkah" label="Langkah-langkah"
@ -118,10 +114,11 @@
<!-- Dapatan --> <!-- Dapatan -->
<FormKit <FormKit
v-model="generatedData.dapatan.value"
type="radio" type="radio"
name="dapatan" name="dapatan"
label="Dapatan" label="Dapatan"
:options="['Tulen', 'Palsu', 'Tidak dapat dikenalpasti']" :options="dapatanOptions"
validation="required" validation="required"
:validation-messages="{ required: 'Dapatan diperlukan' }" :validation-messages="{ required: 'Dapatan diperlukan' }"
/> />
@ -130,7 +127,7 @@
<div> <div>
<h2 class="text-xl font-semibold mb-2">Document Tambahan</h2> <h2 class="text-xl font-semibold mb-2">Document Tambahan</h2>
<FormKit type="list" name="documentTambahan" :value="[]"> <FormKit type="list" name="documentTambahan" :value="[]">
<FormKit type="group" :repeatable="true" :key="index"> <FormKit type="group" :repeatable="true">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<FormKit <FormKit
type="text" type="text"
@ -152,7 +149,7 @@
</div> </div>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<rs-button btn-type="reset" @click="previousPage()" <rs-button variant="danger" btn-type="reset" @click="previousPage()"
>Kembali</rs-button >Kembali</rs-button
> >
<rs-button type="submit" btn-type="submit">Hantar Laporan</rs-button> <rs-button type="submit" btn-type="submit">Hantar Laporan</rs-button>
@ -167,8 +164,9 @@ import { useRoute } from "vue-router";
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { jsPDF } from "jspdf"; import { jsPDF } from "jspdf";
const { $swal } = useNuxtApp();
const route = useRoute(); const route = useRoute();
const bahanBukti = route.params.bahanBukti; const reportID = route.params.reportID;
const generatedData = ref({ const generatedData = ref({
kesId: "", kesId: "",
@ -178,61 +176,87 @@ const generatedData = ref({
pegawai: { pegawai: {
PENYIASAT: { nama: "", pangkat: "", noPegawai: "" }, PENYIASAT: { nama: "", pangkat: "", noPegawai: "" },
PENGHANTAR: { nama: "", pangkat: "", noPegawai: "" }, PENGHANTAR: { nama: "", pangkat: "", noPegawai: "" },
PENERIMA: { nama: "", pangkat: "", noPegawai: "" },
PEMERIKSA: { nama: "", pangkat: "", noPegawai: "" }, PEMERIKSA: { nama: "", pangkat: "", noPegawai: "" },
PENERIMA: { nama: "", pangkat: "", noPegawai: "" },
}, },
peralatan: "", peralatan: "",
langkah2: "", langkah2: "",
dapatan: "", dapatan: "",
documentTambahan: [], documentTambahan: [],
}); });
// State to store dapatan options
const dapatanOptions = ref([]);
onMounted(() => { // Fetch dapatan options from the lookup API
// Simulate fetching system-generated data const fetchDapatanOptions = async () => {
try {
const { data } = await useFetch("/api/lookup?type=dapatan");
if (data.value.statusCode === 200) {
dapatanOptions.value = data.value.data.map((item) => ({
label: item.label,
value: item.value,
}));
} else {
$swal.fire("Error", "Failed to fetch dapatan options.", "error");
}
} catch (error) {
$swal.fire("Error", "Failed to load dapatan options.", "error");
}
};
onMounted(async () => {
try {
const { data } = await useFetch(`/api/laporan/${reportID}`);
if (data.value.statusCode === 200) {
generatedData.value = { generatedData.value = {
kesId: `KES${Math.floor(Math.random() * 1000000) ...generatedData.value, // Keep the default structure
.toString() ...data.value.data, // Merge API data
.padStart(6, "0")}`,
tagNo: `TAG${Math.floor(Math.random() * 10000)
.toString()
.padStart(4, "0")}`,
jenisBrg: ["Dokumen", "Elektronik", "Senjata"][
Math.floor(Math.random() * 3)
],
jenisPemeriksaan: ["Forensik", "Visual", "Kimia"][
Math.floor(Math.random() * 3)
],
pegawai: {
PENYIASAT: generatePegawai("KB"),
PENGHANTAR: generatePegawai(),
PENERIMA: generatePegawai(),
PEMERIKSA: generatePegawai(),
},
};
});
function generatePegawai(role = "") {
const names = ["Ahmad", "Siti", "Mohd", "Nurul", "Lim", "Raj"];
const surnames = ["Abdullah", "Tan", "Kumar", "Lee", "Muthu", "Hassan"];
const pangkat = ["Inspektor", "Sarjan", "Koperal", "Konstabel"];
return {
nama: `${names[Math.floor(Math.random() * names.length)]} ${
surnames[Math.floor(Math.random() * surnames.length)]
}`,
pangkat:
role === "KB"
? "Ketua Bahagian"
: pangkat[Math.floor(Math.random() * pangkat.length)],
noPegawai: `P${Math.floor(Math.random() * 100000)
.toString()
.padStart(5, "0")}`,
}; };
} else {
$swal.fire("Error", "Failed to fetch report data.", "error");
} }
// Fetch dapatan options on mount
await fetchDapatanOptions();
} catch (error) {
$swal.fire("Error", "Failed to load data.", "error");
}
});
// function generatePegawai(role = "") {
// const names = ["Ahmad", "Siti", "Mohd", "Nurul", "Lim", "Raj"];
// const surnames = ["Abdullah", "Tan", "Kumar", "Lee", "Muthu", "Hassan"];
// const pangkat = ["Inspektor", "Sarjan", "Koperal", "Konstabel"];
// return {
// nama: `${names[Math.floor(Math.random() * names.length)]} ${
// surnames[Math.floor(Math.random() * surnames.length)]
// }`,
// pangkat:
// role === "KB"
// ? "Ketua Bahagian"
// : pangkat[Math.floor(Math.random() * pangkat.length)],
// noPegawai: `P${Math.floor(Math.random() * 100000)
// .toString()
// .padStart(5, "0")}`,
// };
// }
const submitForm = async (formData) => { const submitForm = async (formData) => {
console.log("Form submitted:", formData); try {
// Implement your API call or form submission logic here const { data } = await useFetch(`/api/laporan/${reportID}`, {
method: "POST",
body: formData,
});
if (data.value.statusCode === 200) {
$swal.fire("Success", "Report updated successfully", "success");
} else {
$swal.fire("Error", data.value.message, "error");
}
} catch (error) {
$swal.fire("Error", "Failed to submit report", "error");
}
}; };
const generatePDF = () => { const generatePDF = () => {
@ -255,7 +279,11 @@ const generatePDF = () => {
doc.text(`KES ID: ${generatedData.value.kesId}`, 30, 50); doc.text(`KES ID: ${generatedData.value.kesId}`, 30, 50);
doc.text(`TAG NO: ${generatedData.value.tagNo}`, 30, 60); doc.text(`TAG NO: ${generatedData.value.tagNo}`, 30, 60);
doc.text(`Jenis Barang: ${generatedData.value.jenisBrg}`, 30, 70); doc.text(`Jenis Barang: ${generatedData.value.jenisBrg}`, 30, 70);
doc.text(`Jenis Pemeriksaan: ${generatedData.value.jenisPemeriksaan}`, 30, 80); doc.text(
`Jenis Pemeriksaan: ${generatedData.value.jenisPemeriksaan}`,
30,
80
);
// Add officer details // Add officer details
doc.setFontSize(subtitleSize); doc.setFontSize(subtitleSize);
@ -276,21 +304,32 @@ const generatePDF = () => {
doc.text("Butiran Pemeriksaan", 20, yPos); doc.text("Butiran Pemeriksaan", 20, yPos);
doc.setFontSize(normalSize); doc.setFontSize(normalSize);
doc.text(`Peralatan: ${generatedData.value.peralatan || "N/A"}`, 30, yPos + 10); doc.text(
doc.text(`Langkah-langkah: ${generatedData.value.langkah2 || "N/A"}`, 30, yPos + 20); `Peralatan: ${generatedData.value.peralatan || "N/A"}`,
30,
yPos + 10
);
doc.text(
`Langkah-langkah: ${generatedData.value.langkah2 || "N/A"}`,
30,
yPos + 20
);
doc.text(`Dapatan: ${generatedData.value.dapatan || "N/A"}`, 30, yPos + 30); doc.text(`Dapatan: ${generatedData.value.dapatan || "N/A"}`, 30, yPos + 30);
// Add additional documents // Add additional documents
if (generatedData.value.documentTambahan && generatedData.value.documentTambahan.length > 0) { // if (
yPos += 50; // generatedData.value.documentTambahan &&
doc.setFontSize(subtitleSize); // generatedData.value.documentTambahan.length > 0
doc.text("Dokumen Tambahan", 20, yPos); // ) {
// yPos += 50;
// doc.setFontSize(subtitleSize);
// doc.text("Dokumen Tambahan", 20, yPos);
doc.setFontSize(normalSize); // doc.setFontSize(normalSize);
generatedData.value.documentTambahan.forEach((doc, index) => { // generatedData.value.documentTambahan.forEach((doc, index) => {
doc.text(`${index + 1}. ${doc.nama}`, 30, yPos + 10 + (index * 10)); // doc.text(`${index + 1}. ${doc.nama}`, 30, yPos + 10 + index * 10);
}); // });
} // }
// Generate and download the PDF // Generate and download the PDF
doc.save(`Laporan_${generatedData.value.kesId}.pdf`); doc.save(`Laporan_${generatedData.value.kesId}.pdf`);

View File

@ -4,21 +4,25 @@
<rs-card class="p-6"> <rs-card class="p-6">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<h3 class="text-lg font-semibold">Status Semakan</h3> <h3 class="text-lg font-semibold">Status Semakan</h3>
<rs-badge :variant="statusSemakan === 'Selesai' ? 'success' : 'warning'"> <rs-badge
:variant="statusSemakan === 'Selesai' ? 'success' : 'warning'"
>
{{ statusSemakan }} {{ statusSemakan }}
</rs-badge> </rs-badge>
</div> </div>
<div class="flex justify-between items-center mt-4"> <div class="flex justify-between items-center mt-4">
<h3 class="text-lg font-semibold">Status Penerimaan</h3> <h3 class="text-lg font-semibold">Status Penerimaan</h3>
<rs-badge :variant="statusPenerimaan === 'Diterima' ? 'success' : 'danger'"> <rs-badge
:variant="statusPenerimaan === 'Diterima' ? 'success' : 'danger'"
>
{{ statusPenerimaan }} {{ statusPenerimaan }}
</rs-badge> </rs-badge>
</div> </div>
<div class="flex gap-2 justify-end mt-5"> <div class="flex gap-2 mt-5">
<rs-button @click="openSemakModal">Semak</rs-button> <rs-button @click="openSemakModal" variant="primary">Semak</rs-button>
<rs-button @click="openTerimaModal">Terima</rs-button> <rs-button @click="openTerimaModal" variant="success">Terima</rs-button>
<rs-button @click="openTolakModal">Tolak</rs-button> <rs-button @click="openTolakModal" variant="danger">Tolak</rs-button>
</div> </div>
</rs-card> </rs-card>
@ -45,7 +49,7 @@
}" }"
:options-advanced="{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false, filterable: false,
}" }"
advanced advanced
@ -70,13 +74,17 @@
<template v-slot:tindakan="data"> <template v-slot:tindakan="data">
<div class="flex gap-2"> <div class="flex gap-2">
<rs-button <rs-button
@click="openEditModal(data.text, data.index)" @click="openEditModal(data.text.userID, data.text.assignID)"
variant="info" variant="info"
size="sm" size="sm"
> >
<Icon name="ic:baseline-edit" size="1.2rem" /> <Icon name="ic:baseline-edit" size="1.2rem" />
</rs-button> </rs-button>
<rs-button @click="confirmDelete(data.text)" variant="danger" size="sm"> <rs-button
@click="confirmDelete(data.text.userID, data.text.assignID)"
variant="danger"
size="sm"
>
<Icon name="ic:baseline-delete" size="1.2rem" /> <Icon name="ic:baseline-delete" size="1.2rem" />
</rs-button> </rs-button>
</div> </div>
@ -91,6 +99,7 @@
<rs-card class="p-6"> <rs-card class="p-6">
<h3 class="text-lg font-semibold mb-4">Bahan Bukti</h3> <h3 class="text-lg font-semibold mb-4">Bahan Bukti</h3>
<rs-table <rs-table
v-if="evidences.length > 0"
:data="evidences" :data="evidences"
:options="{ :options="{
variant: 'default', variant: 'default',
@ -99,7 +108,7 @@
}" }"
:options-advanced="{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false, filterable: false,
}" }"
advanced advanced
@ -114,9 +123,6 @@
<th>Tindakan</th> <th>Tindakan</th>
</tr> </tr>
</template> </template>
<template v-slot:no="data">
{{ data.text }}
</template>
<template v-slot:jenisBarang="data"> <template v-slot:jenisBarang="data">
{{ data.text }} {{ data.text }}
</template> </template>
@ -140,6 +146,9 @@
</rs-button> </rs-button>
</template> </template>
</rs-table> </rs-table>
<div v-else class="text-center p-10">
<p>Tidak ada bahan bukti yang terlibat.</p>
</div>
</rs-card> </rs-card>
<!-- Add/Edit Modal --> <!-- Add/Edit Modal -->
@ -172,7 +181,6 @@
<template #footer> </template> <template #footer> </template>
</rs-modal> </rs-modal>
<!-- Semak Modal -->
<rs-modal v-model="showSemakModal" @close="closeSemakModal"> <rs-modal v-model="showSemakModal" @close="closeSemakModal">
<template #header> <template #header>
<h3>Semak Maklumat</h3> <h3>Semak Maklumat</h3>
@ -227,8 +235,10 @@
validation="required" validation="required"
/> />
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<rs-button variant="danger" @click="closeSemakModal">Batal</rs-button> <rs-button variant="danger" @click="closeSemakModal"
<rs-button variant="primary" type="submit">Hantar</rs-button> >Batal</rs-button
>
<rs-button variant="primary" btn-type="submit">Hantar</rs-button>
</div> </div>
</FormKit> </FormKit>
@ -255,8 +265,10 @@
}" }"
/> />
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<rs-button variant="danger" @click="closeSemakModal">Batal</rs-button> <rs-button variant="danger" @click="closeSemakModal"
<rs-button variant="primary" type="submit">Hantar</rs-button> >Batal</rs-button
>
<rs-button variant="primary" btn-type="submit">Hantar</rs-button>
</div> </div>
</FormKit> </FormKit>
</template> </template>
@ -314,8 +326,10 @@
validation="required" validation="required"
/> />
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<rs-button variant="danger" @click="closeTerimaModal">Batal</rs-button> <rs-button variant="danger" @click="closeTerimaModal"
<rs-button variant="primary" type="submit">Hantar</rs-button> >Batal</rs-button
>
<rs-button variant="primary" btn-type="submit">Hantar</rs-button>
</div> </div>
</FormKit> </FormKit>
</template> </template>
@ -335,12 +349,7 @@
type="select" type="select"
name="sebabPenolakan" name="sebabPenolakan"
label="Sebab penolakan permohonan" label="Sebab penolakan permohonan"
:options="[ :options="sebabPenolakanOptions"
'Dokumen tidak lengkap',
'Maklumat tidak tepat',
'Tidak memenuhi syarat',
'Lain-lain',
]"
validation="required" validation="required"
:validation-messages="{ :validation-messages="{
required: 'Sila pilih sebab penolakan', required: 'Sila pilih sebab penolakan',
@ -356,8 +365,10 @@
}" }"
/> />
<div class="flex justify-end gap-2 mt-4"> <div class="flex justify-end gap-2 mt-4">
<rs-button variant="secondary" @click="closeTolakModal">Batal</rs-button> <rs-button variant="secondary" @click="closeTolakModal"
<rs-button variant="danger" type="submit">Hantar</rs-button> >Batal</rs-button
>
<rs-button variant="danger" btn-type="submit">Hantar</rs-button>
</div> </div>
</FormKit> </FormKit>
</template> </template>
@ -369,127 +380,294 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from "vue";
definePageMeta({ definePageMeta({
layout: "default", layout: "default",
}); });
const route = useRoute();
const { $swal } = useNuxtApp(); const { $swal } = useNuxtApp();
// Status data // Status data
const statusSemakan = ref("Selesai"); const statusSemakan = ref("Selesai");
const statusPenerimaan = ref("Diterima"); const statusPenerimaan = ref("Diterima");
// Forensic Officers Data // State variables
const showModal = ref(false);
const forensicOfficers = ref([]); const forensicOfficers = ref([]);
const pegawaiOption = ref([]);
// Evidence Data const selectedPegawai = ref(null);
const evidences = ref([ const editMode = ref(false);
{ const currentOfficerID = ref(null);
no: 1, const currentAssignID = ref(null);
jenisBarang: "Dokumen",
tagNo: "TAG001",
keadaan: "Baik",
kuantiti: 3,
tindakan: 1,
},
{
no: 2,
jenisBarang: "Peralatan Elektronik",
tagNo: "TAG002",
keadaan: "Rosak",
kuantiti: 5,
tindakan: 2,
},
]);
// User Roles
const isKetuaBahagian = ref(true); const isKetuaBahagian = ref(true);
const isKetuaJabatan = ref(false); const isKetuaJabatan = ref(false);
// Modal Controls const sebabPenolakanOptions = ref([]);
const showModal = ref(false);
const editMode = ref(false);
const selectedPegawai = ref(null);
// Sample pegawai listing (simulating API response) // Evidence Data
const pegawaiList = ref([]); const evidences = ref([]);
const pegawaiOption = ref([
{
value: null,
label: "Pilih Pegawai",
},
]);
// Fetch pegawai list (simulated API call) // Fetch the status data
const fetchPegawaiList = () => { const fetchStatusData = async () => {
// In a real scenario, this would be an API call try {
pegawaiList.value = [ const { data } = await useFetch(
{ `/api/permohonan/${route.params.noSiri}/status`
id: "PG001", );
nama: "Ahmad bin Ali",
pangkat: "Inspektor", if (data.value.statusCode === 200) {
noPegawai: "PG12345", statusSemakan.value = data.value.data.statusSemakan || "Belum Disemak";
tindakan: 1, statusPenerimaan.value =
}, data.value.data.statusPenerimaan || "Belum Diterima";
{ } else {
id: "PG002", $swal.fire(
nama: "Siti binti Omar", "Error",
pangkat: "Sarjan", "Gagal mendapatkan status semakan dan penerimaan.",
noPegawai: "PG67890", "error"
tindakan: 2, );
}, }
{ } catch (error) {
id: "PG003", $swal.fire("Error", "Gagal memuatkan data status.", "error");
nama: "Muthu a/l Rajan", }
pangkat: "Koperal",
noPegawai: "PG24680",
tindakan: 3,
},
];
}; };
onMounted(() => { // Fetch reports (Bahan Bukti)
fetchPegawaiList(); const fetchReports = async () => {
try {
for (let index = 0; index < pegawaiList.value.length; index++) { const { data } = await useFetch(
pegawaiOption.value.push({ `/api/permohonan/${route.params.noSiri}/reports`
value: pegawaiList.value[index].tindakan,
label: `${pegawaiList.value[index].pangkat} ${pegawaiList.value[index].nama} (${pegawaiList.value[index].noPegawai})`,
});
}
});
// Computed property for form validation
const isFormValid = computed(() => {
return (
// filter pegawaiList based on selectedPegawai
pegawaiList.value.filter((p) => p.tindakan === selectedPegawai.value)
); );
});
// Actions if (data.value.statusCode === 200) {
evidences.value = data.value.data || [];
}
} catch (error) {
console.error("Error fetching reports:", error);
$swal.fire("Error", "Gagal mendapatkan senarai bahan bukti.", "error");
}
};
// Fetch existing forensic officers and available officers
const fetchAssignedOfficers = async () => {
try {
const { data } = await useFetch(
`/api/permohonan/${route.params.noSiri}/forensik/list`
);
if (data.value.statusCode === 200) {
forensicOfficers.value = data.value.data || [];
}
} catch (error) {
console.error("Error fetching forensic officers:", error);
$swal.fire("Error", "Gagal mendapatkan senarai pegawai forensik.", "error");
}
};
const fetchAvailableOfficers = async () => {
try {
const { data } = await useFetch(
`/api/permohonan/${route.params.noSiri}/forensik/available`
);
if (data.value.statusCode === 200) {
pegawaiOption.value = data.value.data || [];
}
} catch (error) {
console.error("Error fetching available officers:", error);
$swal.fire("Error", "Gagal mendapatkan senarai pegawai tersedia.", "error");
}
};
const fetchSebabPenolakanOptions = async () => {
try {
const { data } = await useFetch("/api/lookup?type=sebab_penolakan");
if (data.value.statusCode === 200) {
sebabPenolakanOptions.value = data.value.data;
} else {
$swal.fire("Error", "Failed to fetch sebab penolakan options", "error");
}
} catch (error) {
$swal.fire("Error", "Failed to load lookup data", "error");
}
};
// Open modals
const openAddModal = () => { const openAddModal = () => {
editMode.value = false; editMode.value = false;
selectedPegawai.value = null; selectedPegawai.value = null;
fetchAvailableOfficers();
showModal.value = true; showModal.value = true;
}; };
const openEditModal = (pegawai, index) => { const openEditModal = (userID, assignID) => {
editMode.value = true; editMode.value = true;
selectedPegawai.value = pegawai; currentOfficerID.value = userID;
currentAssignID.value = assignID;
console.log(selectedPegawai.value); selectedPegawai.value = null;
console.log("index", index); fetchAvailableOfficers(); // Get updated available officers for edit mode
showModal.value = true; showModal.value = true;
}; };
const confirmDelete = (userID, assignID) => {
$swal
.fire({
title: "Apakah anda pasti?",
text: "Anda tidak akan dapat mengembalikannya!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Ya, hapus!",
})
.then((result) => {
if (result.isConfirmed) {
deletePegawai(userID, assignID);
}
});
};
// Close modal
const closeModal = () => { const closeModal = () => {
showModal.value = false; showModal.value = false;
selectedPegawai.value = null; selectedPegawai.value = null;
}; };
// Add new officer
const addNewPegawai = async () => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/forensik/add`,
{
method: "POST",
body: { pegawaiID: selectedPegawai.value },
}
);
if (response.data.value.statusCode === 200) {
await fetchAssignedOfficers();
$swal.fire("Berjaya", "Pegawai baru telah ditambah", "success");
closeModal();
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal menambah pegawai.", "error");
}
};
// Edit existing officer
const updatePegawai = async () => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/forensik/edit`,
{
method: "PUT",
body: {
assignID: currentAssignID.value,
newPegawaiID: selectedPegawai.value,
},
}
);
if (response.data.value.statusCode === 200) {
await fetchAssignedOfficers(); // Refresh the list of officers
$swal.fire("Berjaya", "Maklumat pegawai telah dikemaskini", "success");
closeModal();
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal mengemaskini maklumat pegawai.", "error");
}
};
// Delete officer
const deletePegawai = async (officer, assignID) => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/forensik/delete`,
{
method: "DELETE",
body: { assignID: assignID },
}
);
if (response.data.value.statusCode === 200) {
await fetchAssignedOfficers();
$swal.fire("Dihapuskan!", "Pegawai telah dipadam.", "success");
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal memadam pegawai.", "error");
}
};
// Submit Semak form
const handleSemakSubmit = async (formData) => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/semak`,
{
method: "POST",
body: formData,
}
);
if (response.data.value.statusCode === 200) {
$swal.fire("Berjaya", "Maklumat semakan telah disimpan", "success");
await fetchStatusData();
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal menyimpan semakan.", "error");
}
closeSemakModal();
};
// Submit Terima form
const handleTerimaSubmit = async (formData) => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/terima`,
{
method: "POST",
body: formData,
}
);
if (response.data.value.statusCode === 200) {
$swal.fire("Berjaya", "Permohonan telah diterima", "success");
await fetchStatusData();
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal menerima permohonan.", "error");
}
closeTerimaModal();
};
// Submit Tolak form
const handleTolakSubmit = async (formData) => {
try {
const response = await useFetch(
`/api/permohonan/${route.params.noSiri}/tolak`,
{
method: "POST",
body: formData,
}
);
if (response.data.value.statusCode === 200) {
$swal.fire("Berjaya", "Permohonan telah ditolak", "success");
navigateTo("/kemaskini-daftar/senarai");
} else {
$swal.fire("Error", response.data.message, "error");
}
} catch (error) {
$swal.fire("Error", "Gagal menolak permohonan.", "error");
}
closeTolakModal();
};
// Handle form submission (add/edit)
const handleSubmit = () => { const handleSubmit = () => {
if (editMode.value) { if (editMode.value) {
updatePegawai(); updatePegawai();
@ -498,93 +676,15 @@ const handleSubmit = () => {
} }
}; };
const addNewPegawai = () => { // Fetch officers when the component mounts
console.log(selectedPegawai.value); onMounted(() => {
if (selectedPegawai.value) { fetchStatusData();
const selectedPegawai_ = pegawaiList.value.find( fetchAssignedOfficers();
(p) => p.tindakan === selectedPegawai.value fetchReports(); // Fetch reports related to the permohonan
);
if (selectedPegawai_) {
const newPegawai = {
...selectedPegawai_,
tindakan: selectedPegawai_.tindakan,
};
forensicOfficers.value.push(newPegawai);
$swal.fire("Berjaya", "Pegawai baru telah ditambah", "success");
closeModal();
} else {
$swal.fire("Ralat", "Pegawai tidak dijumpai", "error");
}
} else {
$swal.fire("Ralat", "Sila pilih pegawai", "error");
}
};
const updatePegawai = () => {
console.log("masuk uodate");
if (selectedPegawai.value) {
console.log(selectedPegawai.value);
const selectedPegawai_ = pegawaiList.value.find(
(p) => p.tindakan == selectedPegawai.value
);
console.log("selectedPegawai_", selectedPegawai_);
if (selectedPegawai_) {
const pegawaiFromSelectedPegawais = forensicOfficers.value.findIndex(
(officer) => officer.tindakan === selectedPegawai.value
);
console.log("pegawaiFromSelectedPegawais", pegawaiFromSelectedPegawais);
if (pegawai_ !== -1) {
forensicOfficers.value[pegawai_] = {
...selectedPegawai_,
tindakan: selectedPegawai_.tindakan,
};
$swal.fire("Berjaya", "Maklumat pegawai telah dikemaskini", "success");
closeModal();
} else {
$swal.fire("Ralat", "Pegawai tidak dijumpai", "error");
}
} else {
$swal.fire("Ralat", "Pegawai tidak dijumpai", "error");
}
} else {
$swal.fire("Ralat", "Sila pilih pegawai", "error");
}
};
const confirmDelete = (pegawai) => {
$swal
.fire({
title: "Anda pasti?",
text: "Pegawai ini akan dipadamkan.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#3085d6",
confirmButtonText: "Ya, padam",
cancelButtonText: "Batal",
})
.then((result) => {
if (result.isConfirmed) {
deletePegawai(pegawai);
}
}); });
};
const deletePegawai = (pegawai) => {
const index = forensicOfficers.value.findIndex(
(officer) => officer.tindakan === pegawai
);
if (index !== -1) {
forensicOfficers.value.splice(index, 1);
$swal.fire("Dihapuskan!", "Pegawai telah dipadam.", "success");
} else {
$swal.fire("Ralat", "Pegawai tidak dijumpai", "error");
}
};
const generateReport = (bahanBukti) => { const generateReport = (bahanBukti) => {
console.log("Generate Report for:", bahanBukti); console.log("Generate Report for:", bahanBukti);
navigateTo(`/kemaskini-daftar/laporan/${bahanBukti}`); navigateTo(`/kemaskini-daftar/laporan/${bahanBukti}`);
}; };
@ -600,26 +700,11 @@ const closeSemakModal = () => {
}; };
// User role (you might want to fetch this from your auth system) // User role (you might want to fetch this from your auth system)
const userRole = ref("Pegawai Kaunter"); // Change this to 'ketuaBahagian' to test the other form const userRole = ref("Pegawai Kaunter"); // Change this to 'Ketua Bahagian' to test the other form
// For ketua bahagian form // For ketua bahagian form
const kelulusanKetuaBahagian = ref(null); const kelulusanKetuaBahagian = ref(null);
const handleSemakSubmit = (formData) => {
console.log("Semak form submitted:", formData);
// Here you would typically send the data to your API
let successMessage = "";
if (userRole.value === "Pegawai Kaunter") {
successMessage = "Maklumat semakan telah disimpan";
} else if (userRole.value === "Ketua Bahagian") {
const decision =
formData.kelulusanKetuaBahagian === "Diterima" ? "diterima" : "ditolak";
successMessage = `Permohonan telah ${decision}. Ulasan: ${formData.ulasanKetuaBahagian}`;
}
$swal.fire("Berjaya", successMessage, "success");
closeSemakModal();
};
// Terima Modal Controls // Terima Modal Controls
const showTerimaModal = ref(false); const showTerimaModal = ref(false);
@ -631,53 +716,19 @@ const closeTerimaModal = () => {
showTerimaModal.value = false; showTerimaModal.value = false;
}; };
const handleTerimaSubmit = (formData) => {
console.log("Terima form submitted:", formData);
// Here you would typically send the data to your API
let successMessage = "Permohonan telah diterima.";
// Check if any of the radio button answers are 'Tidak'
const hasNegativeResponse = [
"peralatanBaik",
"pegawaiBerkelayakan",
"kaedahDapatDilakukan",
"tugasanDiterima",
].some((field) => formData[field] === "Tidak");
if (hasNegativeResponse) {
successMessage += " Namun, terdapat beberapa perkara yang perlu diberi perhatian.";
}
successMessage += ` Ulasan: ${formData.ulasanPegawaiKaunter}`;
$swal.fire("Berjaya", successMessage, "success");
closeTerimaModal();
};
// Tolak Modal Controls // Tolak Modal Controls
const showTolakModal = ref(false); const showTolakModal = ref(false);
const openTolakModal = () => { const openTolakModal = async () => {
await fetchSebabPenolakanOptions();
showTolakModal.value = true; showTolakModal.value = true;
}; };
const closeTolakModal = () => { const closeTolakModal = () => {
showTolakModal.value = false; showTolakModal.value = false;
}; };
const handleTolakSubmit = (formData) => {
console.log("Tolak form submitted:", formData);
// Here you would typically send the data to your API
let rejectionReason = formData.sebabPenolakan;
if (rejectionReason === "Lain-lain") {
rejectionReason += `: ${formData.lainLainSebab}`;
}
const successMessage = `Permohonan telah ditolak. Sebab: ${rejectionReason}`;
$swal.fire("Berjaya", successMessage, "success");
closeTolakModal();
};
</script> </script>
<style lang="scss" scoped></style> <style scoped>
/* Your existing styles */
</style>

View File

@ -7,16 +7,16 @@
<rs-card class="mt-4 py-2"> <rs-card class="mt-4 py-2">
<rs-table <rs-table
:data="tableData" :data="tableData"
:options='{ :options="{
variant: "default", variant: 'default',
striped: true, striped: true,
borderless: true borderless: true,
}' }"
:options-advanced='{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false filterable: false,
}' }"
advanced advanced
> >
<template v-slot:header> <template v-slot:header>
@ -73,50 +73,29 @@
</template> </template>
<script setup> <script setup>
import { ref } from "vue";
const { $swal } = useNuxtApp(); const { $swal } = useNuxtApp();
definePageMeta({ definePageMeta({
title: "Senarai Permohonan", title: "Senarai Permohonan",
}); });
const tableData = ref([ // Reactive variable to store table data
{ const tableData = ref([]);
no: 1,
noSiri: "1234567890", // Fetch permohonan list from API
tarikhMasa: "2024-01-01 12:00:00", const fetchPermohonan = async () => {
status: "Aktif", try {
butiran: 1, const response = await useFetch("/api/permohonan");
}, if (response.data.value.statusCode === 200) {
{ // Populate tableData with the fetched permohonan list
no: 2, tableData.value = response.data.value.data;
noSiri: "0987654321", } else {
tarikhMasa: "2024-02-01 14:30:00", console.error(response.data.value.message);
status: "Aktif", }
butiran: 2, } catch (error) {
}, console.error("Error fetching permohonan data:", error);
{ }
no: 3, };
noSiri: "1122334455",
tarikhMasa: "2024-03-01 09:15:00",
status: "Aktif",
butiran: 3,
},
{
no: 4,
noSiri: "5566778899",
tarikhMasa: "2024-04-01 16:45:00",
status: "Aktif",
butiran: 4,
},
{
no: 5,
noSiri: "6677889900",
tarikhMasa: "2024-05-01 11:00:00",
status: "Aktif",
butiran: 5,
},
]);
const permohonanBaru = () => { const permohonanBaru = () => {
navigateTo("/permohonan-temujanji/baru"); navigateTo("/permohonan-temujanji/baru");
@ -131,9 +110,8 @@ const lihat = (item) => {
navigateTo(`/kemaskini-daftar/maklumat/${item}`); navigateTo(`/kemaskini-daftar/maklumat/${item}`);
}; };
const hapus = (item) => { const hapus = async (noSiri) => {
$swal const confirmation = await $swal.fire({
.fire({
title: "Anda pasti?", title: "Anda pasti?",
text: "Anda tidak akan dapat memulihkan semula data ini!", text: "Anda tidak akan dapat memulihkan semula data ini!",
icon: "warning", icon: "warning",
@ -142,18 +120,30 @@ const hapus = (item) => {
cancelButtonColor: "#d33", cancelButtonColor: "#d33",
confirmButtonText: "Ya, hapuskan!", confirmButtonText: "Ya, hapuskan!",
cancelButtonText: "Batal", cancelButtonText: "Batal",
})
.then((result) => {
if (result.isConfirmed) {
// Perform deletion logic here
console.log("Deleting item:", item);
// Remove the item from the tableData
const index = tableData.value.findIndex((row) => row.noSiri === item);
if (index !== -1) {
tableData.value.splice(index, 1);
}
$swal.fire("Dihapuskan!", "Data telah dihapuskan.", "success");
}
}); });
if (confirmation.isConfirmed) {
try {
const response = await useFetch(`/api/permohonan/${noSiri}`, {
method: "DELETE",
});
if (response.data.value.statusCode === 200) {
// Remove the deleted permohonan from tableData
tableData.value = tableData.value.filter(
(row) => row.noSiri !== noSiri
);
$swal.fire("Dihapuskan!", response.data.value.message, "success");
} else {
$swal.fire("Error!", response.data.value.message, "error");
}
} catch (error) {
$swal.fire("Error!", "Failed to delete permohonan.", "error");
}
}
}; };
// Fetch the permohonan list when the component is mounted
onMounted(() => {
fetchPermohonan();
});
</script> </script>

View File

@ -98,7 +98,11 @@
<tbody> <tbody>
<tr v-for="(barang, index) in barangList" :key="index"> <tr v-for="(barang, index) in barangList" :key="index">
<td class="border border-gray-300 p-2"> <td class="border border-gray-300 p-2">
{{ getJenisBarangLabel(barang.jenisBarangDetail) }} {{
barang.jenisBarangDetailLabel
? barang.jenisBarangDetailLabel
: barang.jenisBarangDetail
}}
</td> </td>
<td class="border border-gray-300 p-2"> <td class="border border-gray-300 p-2">
{{ barang.kuantitiBarang }} {{ barang.kuantitiBarang }}
@ -369,9 +373,19 @@ const cancelBarangModal = () => {
const saveBarangModal = () => { const saveBarangModal = () => {
if (editingBarangIndex.value === null) { if (editingBarangIndex.value === null) {
barangList.value.push({ ...currentBarang.value }); barangList.value.push({
...currentBarang.value,
jenisBarangDetailLabel: getJenisBarangLabel(
currentBarang.value.jenisBarangDetail
),
});
} else { } else {
barangList.value[editingBarangIndex.value] = { ...currentBarang.value }; barangList.value[editingBarangIndex.value] = {
...currentBarang.value,
jenisBarangDetailLabel: getJenisBarangLabel(
currentBarang.value.jenisBarangDetail
),
};
} }
isBarangModalOpen.value = false; isBarangModalOpen.value = false;
}; };
@ -448,7 +462,7 @@ const submitData = async (isDraft) => {
// Redirect to senarai page after successful submission // Redirect to senarai page after successful submission
if (!isDraft) { if (!isDraft) {
router.push('/permohonan-temujanji/senarai'); router.push("/permohonan-temujanji/senarai");
} }
} else { } else {
$swal.fire({ $swal.fire({

View File

@ -97,7 +97,11 @@
<tbody> <tbody>
<tr v-for="(barang, index) in barangList" :key="index"> <tr v-for="(barang, index) in barangList" :key="index">
<td class="border border-gray-300 p-2"> <td class="border border-gray-300 p-2">
{{ getJenisBarangLabel(barang.jenisBarangDetail) }} {{
barang.jenisBarangDetailLabel
? barang.jenisBarangDetailLabel
: barang.jenisBarangDetail
}}
</td> </td>
<td class="border border-gray-300 p-2"> <td class="border border-gray-300 p-2">
{{ barang.kuantitiBarang }} {{ barang.kuantitiBarang }}
@ -406,9 +410,19 @@ const cancelBarangModal = () => {
const saveBarangModal = () => { const saveBarangModal = () => {
if (editingBarangIndex.value === null) { if (editingBarangIndex.value === null) {
barangList.value.push({ ...currentBarang.value }); barangList.value.push({
...currentBarang.value,
jenisBarangDetailLabel: getJenisBarangLabel(
currentBarang.value.jenisBarangDetail
),
});
} else { } else {
barangList.value[editingBarangIndex.value] = { ...currentBarang.value }; barangList.value[editingBarangIndex.value] = {
...currentBarang.value,
jenisBarangDetailLabel: getJenisBarangLabel(
currentBarang.value.jenisBarangDetail
),
};
} }
isBarangModalOpen.value = false; isBarangModalOpen.value = false;
}; };

View File

@ -20,7 +20,6 @@
}" }"
:options-advanced="{ :options-advanced="{
sortable: true, sortable: true,
responsive: true,
filterable: false, filterable: false,
}" }"
advanced advanced
@ -62,10 +61,7 @@
<template v-slot:butiran="data"> <template v-slot:butiran="data">
<div <div
class="flex flex-wrap gap-2" class="flex flex-wrap gap-2"
v-if=" v-if="data.value.status === 'Permohonan Draf'"
data.value.status !== 'Sah' &&
data.value.status !== 'Permohonan Dihantar'
"
> >
<!-- Button to navigate to the "Kemaskini" page for the selected permohonan --> <!-- Button to navigate to the "Kemaskini" page for the selected permohonan -->
<rs-button <rs-button

View File

@ -106,8 +106,8 @@ model permohonan {
penghantar penghantar? @relation(fields: [penghantarID], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "permohonan_ibfk_1") penghantar penghantar? @relation(fields: [penghantarID], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "permohonan_ibfk_1")
permohonan_assign_forensik permohonan_assign_forensik[] permohonan_assign_forensik permohonan_assign_forensik[]
permohonan_jenis_barang permohonan_jenis_barang[] permohonan_jenis_barang permohonan_jenis_barang[]
permohonan_penerimaan permohonan_penerimaan[] permohonan_penerimaan permohonan_penerimaan?
permohonan_semakan permohonan_semakan[] permohonan_semakan permohonan_semakan?
report report[] report report[]
@@index([pemohonID], map: "idx_pemohon") @@index([pemohonID], map: "idx_pemohon")
@ -129,25 +129,24 @@ model permohonan_assign_forensik {
model permohonan_penerimaan { model permohonan_penerimaan {
penerimaanID Int @id @default(autoincrement()) penerimaanID Int @id @default(autoincrement())
permohonanID Int permohonanID Int @unique(map: "permohonanID")
peralatan_keadaan_baik Int peralatan_keadaan_baik Int
pegawai_berkelayakan Int pegawai_berkelayakan Int
kaedah_dpt_dilakukan Int kaedah_dpt_dilakukan Int
subkontrak_diperlukan Int subkontrak_diperlukan Int
tugasan_diterima Int tugasan_diterima Int
ulasan_pegawai Int? ulasan_pegawai String? @db.Text
create_at DateTime @db.DateTime(0) create_at DateTime @db.DateTime(0)
diterima_oleh Int diterima_oleh Int
permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penerimaan_ibfk_1") permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penerimaan_ibfk_1")
user user @relation(fields: [diterima_oleh], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penerimaan_ibfk_2") user user @relation(fields: [diterima_oleh], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penerimaan_ibfk_2")
@@index([diterima_oleh], map: "diterima_oleh") @@index([diterima_oleh], map: "diterima_oleh")
@@index([permohonanID], map: "permohonanID")
} }
model permohonan_penolakan { model permohonan_penolakan {
penolakanID Int @id @default(autoincrement()) penolakanID Int @id @default(autoincrement())
permohonanID Int permohonanID Int @unique(map: "permohonanID")
sebab_penolakan Int sebab_penolakan Int
lain_sebab String? @db.VarChar(255) lain_sebab String? @db.VarChar(255)
create_at DateTime? @db.DateTime(0) create_at DateTime? @db.DateTime(0)
@ -161,20 +160,19 @@ model permohonan_penolakan {
model permohonan_semakan { model permohonan_semakan {
semakanID Int @id @default(autoincrement()) semakanID Int @id @default(autoincrement())
permohonanID Int permohonanID Int @unique(map: "permohonanID")
peralatan_keadaan_baik Int peralatan_keadaan_baik Int?
pegawai_berkelayakan Int pegawai_berkelayakan Int?
kaedah_dpt_dilakukan Int kaedah_dpt_dilakukan Int?
subkontrak_diperlukan Int subkontrak_diperlukan Int?
tugasan_diterima Int tugasan_diterima Int?
ulasan_pegawai String? @db.Text ulasan_pegawai String? @db.Text
create_at Int create_at DateTime? @db.DateTime(0)
disemak_oleh Int disemak_oleh Int?
permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_semakan_ibfk_1") permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_semakan_ibfk_1")
user user @relation(fields: [disemak_oleh], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_semakan_ibfk_2") user user? @relation(fields: [disemak_oleh], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_semakan_ibfk_2")
@@index([disemak_oleh], map: "disemak_oleh") @@index([disemak_oleh], map: "disemak_oleh")
@@index([permohonanID], map: "permohonanID")
} }
model role { model role {
@ -275,8 +273,11 @@ model report {
reportID Int @id @default(autoincrement()) reportID Int @id @default(autoincrement())
permohonanID Int permohonanID Int
jenis_barang Int jenis_barang Int
tanda_barang String? @db.VarChar(255)
keadaan_barang String? @db.VarChar(255)
kuantiti_barang Int?
peralatan String? @db.VarChar(255) peralatan String? @db.VarChar(255)
langkah_langkah Int? langkah_langkah String? @db.VarChar(255)
gambarID Int? gambarID Int?
ulasan String? @db.Text ulasan String? @db.Text
dapatan Int? dapatan Int?

View File

@ -0,0 +1,54 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params; // Extract the noSiri from the URL
const body = await readBody(event); // Read the request body
const { ringkasanKenyataanKes, noKertasSiasatan, noLaporanPolis } = body;
// 1. Validate the input fields (all three fields must be provided)
if (!ringkasanKenyataanKes || !noKertasSiasatan || !noLaporanPolis) {
return {
statusCode: 400,
message:
"Setiap medan mandatori yang bertanda * telah diisi. (Ralat CMN-E001)",
};
}
try {
// 2. Check if the permohonan exists by its `no_siri`
const existingPermohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
// If no `permohonan` is found, return a 404 error
if (!existingPermohonan) {
return {
statusCode: 404,
message: `Permohonan with noSiri ${noSiri} not found.`,
};
}
// 3. Proceed to update only the allowed fields (ringkasanKenyataanKes, noKertasSiasatan, noLaporanPolis)
const updatedPermohonan = await prisma.permohonan.update({
where: { no_siri: noSiri },
data: {
ringkasan_kenyataan_kes: ringkasanKenyataanKes,
no_kertas_siasatan: noKertasSiasatan,
no_laporan_polis: noLaporanPolis,
modified_at: new Date(), // Update the modified_at timestamp
},
});
// 4. Return success response
return {
statusCode: 200,
message: "Permohonan telah berjaya dikemaskini.",
data: updatedPermohonan,
};
} catch (error) {
console.error("Error updating permohonan:", error);
return {
statusCode: 500,
message: "Gagal mengemaskini permohonan. Sila cuba lagi.",
};
}
});

View File

@ -0,0 +1,103 @@
// Path: /api/report/[reportID].get.js
export default defineEventHandler(async (event) => {
const { reportID } = event.context.params;
try {
const report = await prisma.report.findUnique({
where: { reportID: parseInt(reportID) },
include: {
permohonan: {
include: {
penghantar: true,
pemohon: {
include: {
user: true,
},
},
},
},
lookup_report_jenis_barangTolookup: true,
lookup_report_dapatanTolookup: {
select: {
lookupID: true,
lookupValue: true,
},
},
report_doc_support: {
include: {
document: true,
},
},
},
});
console.log(report);
if (!report) {
return { statusCode: 404, message: "Report not found" };
}
// Format the data for the frontend
const reportData = {
kesId: report.permohonan.no_siri,
tagNo: report.tanda_barang,
jenisBrg: report.lookup_report_jenis_barangTolookup.lookupValue,
jenisPemeriksaan: "Forensik", // Assuming it's static for now
pegawai: {
PENYIASAT: {
nama: report.permohonan.pemohon?.user?.userFullName || "",
pangkat: report.permohonan.pemohon?.pangkat_pemohon || "",
noPegawai: report.permohonan.pemohon?.no_pegawai_pemohon || "",
},
PENGHANTAR: {
nama: report.permohonan.penghantar_sama_dengan_pemohon
? report.permohonan.pemohon?.user?.userFullName || ""
: report.permohonan.penghantar?.nama_penghantar || "",
pangkat: report.permohonan.penghantar_sama_dengan_pemohon
? report.permohonan.pemohon?.pangkat_pemohon || ""
: report.permohonan.penghantar?.pangkat_penghantar || "",
noPegawai: report.permohonan.penghantar_sama_dengan_pemohon
? report.permohonan.pemohon?.no_pegawai_pemohon || ""
: report.permohonan.penghantar?.no_pegawai_penghantar || "",
},
PEMERIKSA: {
nama: report.permohonan.pemerikasa?.user?.userFullName || "",
pangkat: report.permohonan.pemerikasa?.pangkat_pemerikasa || "",
noPegawai: report.permohonan.pemerikasa?.no_pegawai_pemerikasa || "",
},
PENERIMA: {
nama: report.permohonan.penerima?.user?.userFullName || "",
pangkat: report.permohonan.penerima?.pangkat_penerima || "",
noPegawai: report.permohonan.penerima?.no_pegawai_penerima || "",
},
// Fill in other roles accordingly
},
peralatan: report.peralatan,
langkah2: report.langkah_langkah,
dapatan: {
value: report.lookup_report_dapatanTolookup?.lookupID,
label: report.lookup_report_dapatanTolookup?.lookupValue,
},
documentTambahan: report.report_doc_support
? [
{
nama: report.report_doc_support[0].document.documentName,
file: report.report_doc_support[0].document.documentURL,
},
]
: [
{
nama: "",
file: "",
}
],
};
return { statusCode: 200, data: reportData };
} catch (error) {
console.log(error);
return { statusCode: 500, message: error.message };
}
});

View File

@ -0,0 +1,63 @@
// Path: /api/report/[reportID].post.js
export default defineEventHandler(async (event) => {
const { reportID } = event.context.params;
const body = await readBody(event);
const {
peralatan,
langkah2,
dapatan, // This should correspond to lookupID in the `lookup` table
documentTambahan, // Array of documents
} = body;
console.log(body);
try {
// Update the report in the database
const updatedReport = await prisma.report.update({
where: { reportID: parseInt(reportID) },
data: {
peralatan,
langkah_langkah: langkah2,
dapatan: parseInt(dapatan), // Assuming this is a lookupID from the `lookup` table
},
});
// Handle document uploads (if necessary)
if (documentTambahan && documentTambahan.length > 0) {
// Create documents first
const createdDocuments = await prisma.document.createMany({
data: documentTambahan.map((doc) => ({
documentName: doc.nama,
})),
skipDuplicates: true,
});
// Get the IDs of the newly created documents
const newDocumentIds = await prisma.document.findMany({
where: {
documentName: {
in: documentTambahan.map((doc) => doc.nama),
},
},
select: {
documentID: true,
documentName: true,
},
});
// Update report_doc_support table
await prisma.report_doc_support.createMany({
data: newDocumentIds.map((doc) => ({
reportID: updatedReport.reportID,
documentID: doc.documentID,
})),
});
}
return { statusCode: 200, message: "Report updated successfully" };
} catch (error) {
console.log(error);
return { statusCode: 500, message: "Failed to update report" };
}
});

View File

@ -1,3 +1,4 @@
// path: /api/lookup?type=jenis_barang
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { type } = getQuery(event); // Get lookup type from query params, e.g., jenis_barang, dapatan const { type } = getQuery(event); // Get lookup type from query params, e.g., jenis_barang, dapatan
@ -20,10 +21,22 @@ export default defineEventHandler(async (event) => {
lookupValue: true, lookupValue: true,
}, },
}); });
if (!lookups) {
return {
statusCode: 404,
message: "Lookup data not found",
};
}
// Convert snake_case to Title Case: e.g., jenis_barang_siber -> Jenis Barang Siber
const defaultTitle = type
.split("_")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
// Transform the lookups data to the required format // Transform the lookups data to the required format
const transformedLookups = [ const transformedLookups = [
{ label: "", value: null }, // Add an empty option as the first item { label: `Sila Pilih ${defaultTitle}`, value: null }, // Add an empty option as the first item
...lookups.map((lookup) => ({ ...lookups.map((lookup) => ({
label: lookup.lookupValue, label: lookup.lookupValue,
value: lookup.lookupID, value: lookup.lookupID,

View File

@ -22,7 +22,20 @@ export default defineEventHandler(async (event) => {
}, },
}, },
penghantar: true, penghantar: true,
report: true, // Assuming 'report' is where the `barang` (items) are stored report: {
select: {
jenis_barang: true,
lookup_report_jenis_barangTolookup: {
select: {
lookupID: true,
lookupValue: true,
},
},
tanda_barang: true,
keadaan_barang: true,
kuantiti_barang: true,
},
},
}, },
}); });
@ -47,7 +60,10 @@ export default defineEventHandler(async (event) => {
ringkasanKenyataanKes: permohonan.ringkasan_kenyataan_kes || "", ringkasanKenyataanKes: permohonan.ringkasan_kenyataan_kes || "",
bilangan: permohonan.bilangan || 0, bilangan: permohonan.bilangan || 0,
barangList: permohonan.report.map((barang) => ({ barangList: permohonan.report.map((barang) => ({
jenisBarangDetail: barang.jenis_barang || "", jenisBarangDetail:
barang.lookup_report_jenis_barangTolookup.lookupID || "",
jenisBarangDetailLabel:
barang.lookup_report_jenis_barangTolookup.lookupValue || "",
tandaBarang: barang.tanda_barang || "", tandaBarang: barang.tanda_barang || "",
keadaanBarang: barang.keadaan_barang || "", keadaanBarang: barang.keadaan_barang || "",
kuantitiBarang: barang.kuantiti_barang || 0, kuantitiBarang: barang.kuantiti_barang || 0,

View File

@ -127,21 +127,13 @@ export default defineEventHandler(async (event) => {
}); });
for (const barang of barangList) { for (const barang of barangList) {
// await prisma.report.create({
// data: {
// permohonanID: updatedPermohonan.id,
// jenis_barang: barang.jenisBarangDetail,
// kuantiti_barang: barang.kuantitiBarang,
// tanda_barang: barang.tandaBarang,
// keadaan_barang: barang.keadaanBarang,
// create_by: userID,
// create_at: new Date(),
// },
// });
await prisma.report.create({ await prisma.report.create({
data: { data: {
permohonanID: updatedPermohonan.id, permohonanID: updatedPermohonan.id,
jenis_barang: barang.jenisBarangDetail, jenis_barang: barang.jenisBarangDetail,
kuantiti_barang: parseInt(barang.kuantitiBarang),
tanda_barang: barang.tandaBarang,
keadaan_barang: barang.keadaanBarang,
create_by: userID, create_by: userID,
create_at: new Date(), create_at: new Date(),
}, },

View File

@ -0,0 +1,42 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event);
const { pegawaiID } = body;
try {
// Ensure that the permohonan exists
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Ensure that the pegawai is not already assigned
const existingAssignment =
await prisma.permohonan_assign_forensik.findFirst({
where: {
permohonanID: permohonan.id,
pegawai_forensikID: pegawaiID,
},
});
if (existingAssignment) {
return { statusCode: 400, message: "Pegawai sudah ditugaskan." };
}
// Assign the pegawai to this permohonan
await prisma.permohonan_assign_forensik.create({
data: {
permohonanID: permohonan.id,
pegawai_forensikID: pegawaiID,
},
});
return { statusCode: 200, message: "Pegawai berjaya ditambah." };
} catch (error) {
console.error(error);
return { statusCode: 500, message: "Error adding forensic officer." };
}
});

View File

@ -0,0 +1,51 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
try {
// Fetch the list of assigned officers
const assignedOfficers = await prisma.permohonan_assign_forensik.findMany({
where: { permohonan: { no_siri: noSiri } },
select: { pegawai_forensikID: true },
});
const assignedOfficerIDs = assignedOfficers.map(
(officer) => officer.pegawai_forensikID
);
// Fetch all pegawai forensik that are not assigned to this permohonan
const availablePegawai = await prisma.user.findMany({
where: {
userID: { notIn: assignedOfficerIDs },
userStatus: "ACTIVE",
userrole: {
some: {
role: {
roleName: "Pegawai Forensik",
},
},
},
},
select: {
userID: true,
userFullName: true,
userUsername: true,
},
});
return {
statusCode: 200,
data: [
{ value: "", label: "Sila Pilih Pegawai" },
...availablePegawai.map((pegawai) => ({
value: pegawai.userID,
label: `${pegawai.userFullName}`,
})),
],
};
} catch (error) {
return {
statusCode: 500,
message: "Error fetching available forensic officers.",
};
}
});

View File

@ -0,0 +1,27 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event);
const { assignID } = body;
try {
// Fetch the existing permohonan
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Delete the pegawai assignment
await prisma.permohonan_assign_forensik.delete({
where: {
assignID: assignID,
},
});
return { statusCode: 200, message: "Pegawai berjaya dipadamkan." };
} catch (error) {
return { statusCode: 500, message: "Error deleting forensic officer." };
}
});

View File

@ -0,0 +1,46 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event);
const { oldPegawaiID, newPegawaiID, assignID } = body;
try {
// Fetch the existing permohonan
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Check if the new pegawai is already assigned
const existingAssignment =
await prisma.permohonan_assign_forensik.findFirst({
where: {
permohonanID: permohonan.id,
pegawai_forensikID: newPegawaiID,
},
});
console.log("existingAssignment", existingAssignment);
if (existingAssignment) {
return { statusCode: 400, message: "Pegawai baru sudah ditugaskan." };
}
// Update the old pegawai with the new pegawai
await prisma.permohonan_assign_forensik.update({
where: {
assignID: assignID,
},
data: {
pegawai_forensikID: newPegawaiID,
},
});
return { statusCode: 200, message: "Pegawai berjaya dikemaskini." };
} catch (error) {
console.log(error);
return { statusCode: 500, message: "Error updating forensic officer." };
}
});

View File

@ -0,0 +1,45 @@
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
try {
// Fetch the permohonan by noSiri
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
select: {
permohonan_assign_forensik: {
include: {
user: {
select: {
userID: true,
userFullName: true,
userUsername: true,
},
},
},
},
},
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Return the list of assigned forensic officers
const forensicOfficers = permohonan.permohonan_assign_forensik.map(
(assignment) => ({
nama: assignment.user.userFullName,
tindakan: {
userID: assignment.user.userID,
assignID: assignment.assignID,
},
})
);
return {
statusCode: 200,
data: forensicOfficers,
};
} catch (error) {
return { statusCode: 500, message: "Error fetching forensic officers." };
}
});

View File

@ -0,0 +1,38 @@
// File: /api/permohonan/[noSiri]/reports.get.js
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
try {
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
select: {
report: {
include: {
lookup_report_jenis_barangTolookup: true, // To get the jenis barang lookup data
},
},
},
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Map reports to the frontend format
const reports = permohonan.report.map((report) => ({
jenisBarang: report.lookup_report_jenis_barangTolookup.lookupValue,
tagNo: report.tanda_barang,
keadaan: report.keadaan_barang,
kuantiti: report.kuantiti_barang,
tindakan: report.reportID,
}));
return {
statusCode: 200,
data: reports,
};
} catch (error) {
return { statusCode: 500, message: "Error fetching reports." };
}
});

View File

@ -0,0 +1,61 @@
// File: /api/permohonan/[noSiri]/semak.put.js
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event); // Get form data from frontend
try {
// Get the current user (assuming user authentication is handled)
const user = event.context.user;
// Find the permohonan by its noSiri
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Update or create the permohonan semakan entry
await prisma.permohonan_semakan.upsert({
where: {
permohonanID: permohonan.id,
},
update: {
peralatan_keadaan_baik: body.peralatanBaik === "Ya" ? 1 : 0,
pegawai_berkelayakan: body.pegawaiBerkelayakan === "Ya" ? 1 : 0,
kaedah_dpt_dilakukan: body.kaedahDapatDilakukan === "Ya" ? 1 : 0,
subkontrak_diperlukan: body.subkontrakDiperlukan === "Ya" ? 1 : 0,
tugasan_diterima: body.tugasanDiterima === "Ya" ? 1 : 0,
ulasan_pegawai: body.ulasanPegawaiKaunter,
disemak_oleh: user.userID, // Use the authenticated user ID
create_at: new Date(),
},
create: {
permohonanID: permohonan.id,
peralatan_keadaan_baik: body.peralatanBaik === "Ya" ? 1 : 0,
pegawai_berkelayakan: body.pegawaiBerkelayakan === "Ya" ? 1 : 0,
kaedah_dpt_dilakukan: body.kaedahDapatDilakukan === "Ya" ? 1 : 0,
subkontrak_diperlukan: body.subkontrakDiperlukan === "Ya" ? 1 : 0,
tugasan_diterima: body.tugasanDiterima === "Ya" ? 1 : 0,
ulasan_pegawai: body.ulasanPegawaiKaunter,
disemak_oleh: user.userID,
create_at: new Date(),
},
});
// Update the status of the permohonan to "Permohonan Disemak"
await prisma.permohonan.update({
where: { no_siri: noSiri },
data: { status_permohonan: "Permohonan Disemak" },
});
return {
statusCode: 200,
message: "Maklumat semakan berjaya dikemaskini.",
};
} catch (error) {
console.error(error);
return { statusCode: 500, message: "Gagal mengemaskini maklumat semakan." };
}
});

View File

@ -0,0 +1,49 @@
export default defineEventHandler(async (event) => {
// Extract the `noSiri` from the URL params
const { noSiri } = event.context.params;
try {
// Fetch the Semakan status
const semakan = await prisma.permohonan_semakan.findFirst({
where: {
permohonan: {
no_siri: noSiri,
},
},
select: {
semakanID: true, // Checking if semakan exists
},
});
// Fetch the Penerimaan status
const penerimaan = await prisma.permohonan_penerimaan.findFirst({
where: {
permohonan: {
no_siri: noSiri,
},
},
select: {
penerimaanID: true, // Checking if penerimaan exists
},
});
// Determine statuses based on existence
const statusSemakan = semakan ? "Selesai" : "Belum Disemak";
const statusPenerimaan = penerimaan ? "Diterima" : "Belum Diterima";
// Return the statuses
return {
statusCode: 200,
data: {
statusSemakan,
statusPenerimaan,
},
};
} catch (error) {
// Return an error if something goes wrong
return {
statusCode: 500,
message: "Gagal mendapatkan status",
};
}
});

View File

@ -0,0 +1,64 @@
// File: /api/permohonan/[noSiri]/terima.put.js
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event); // Get form data from frontend
try {
// Get the current user (assuming user authentication is handled)
const user = event.context.user;
// Find the permohonan by its noSiri
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Create or update the permohonan penerimaan entry
await prisma.permohonan_penerimaan.upsert({
where: {
permohonanID: permohonan.id,
},
update: {
peralatan_keadaan_baik: body.peralatanBaik === "Ya" ? 1 : 0,
pegawai_berkelayakan: body.pegawaiBerkelayakan === "Ya" ? 1 : 0,
kaedah_dpt_dilakukan: body.kaedahDapatDilakukan === "Ya" ? 1 : 0,
subkontrak_diperlukan: body.subkontrakDiperlukan === "Ya" ? 1 : 0,
tugasan_diterima: body.tugasanDiterima === "Ya" ? 1 : 0,
ulasan_pegawai: body.ulasanPegawaiKaunter,
diterima_oleh: user.userID,
create_at: new Date(), // Store current date
},
create: {
permohonanID: permohonan.id,
peralatan_keadaan_baik: body.peralatanBaik === "Ya" ? 1 : 0,
pegawai_berkelayakan: body.pegawaiBerkelayakan === "Ya" ? 1 : 0,
kaedah_dpt_dilakukan: body.kaedahDapatDilakukan === "Ya" ? 1 : 0,
subkontrak_diperlukan: body.subkontrakDiperlukan === "Ya" ? 1 : 0,
tugasan_diterima: body.tugasanDiterima === "Ya" ? 1 : 0,
ulasan_pegawai: body.ulasanPegawaiKaunter,
diterima_oleh: user.userID,
create_at: new Date(),
},
});
// Update the status of the permohonan to "Diterima"
await prisma.permohonan.update({
where: { no_siri: noSiri },
data: { status_permohonan: "Permohonan Diterima" },
});
return {
statusCode: 200,
message: "Maklumat penerimaan berjaya dikemaskini.",
};
} catch (error) {
console.error(error);
return {
statusCode: 500,
message: "Gagal mengemaskini maklumat penerimaan.",
};
}
});

View File

@ -0,0 +1,56 @@
// File: /api/permohonan/[noSiri]/tolak.put.js
export default defineEventHandler(async (event) => {
const { noSiri } = event.context.params;
const body = await readBody(event); // Get form data from frontend
try {
// Get the current user (assuming user authentication is handled)
const user = event.context.user;
// Find the permohonan by its noSiri
const permohonan = await prisma.permohonan.findUnique({
where: { no_siri: noSiri },
});
if (!permohonan) {
return { statusCode: 404, message: "Permohonan tidak dijumpai." };
}
// Create or update the permohonan penolakan entry
await prisma.permohonan_penolakan.upsert({
where: {
permohonanID: permohonan.id,
},
update: {
sebab_penolakan: parseInt(body.sebabPenolakan), // Assuming lookupID is passed
lain_sebab: body.lainLainSebab || null,
ditolak_oleh: user.userID,
create_at: new Date(),
},
create: {
permohonanID: permohonan.id,
sebab_penolakan: parseInt(body.sebabPenolakan),
lain_sebab: body.lainLainSebab || null,
ditolak_oleh: user.userID,
create_at: new Date(),
},
});
// Update the status of the permohonan to "Permohonan Ditolak"
await prisma.permohonan.update({
where: { no_siri: noSiri },
data: { status_permohonan: "Permohonan Ditolak" },
});
return {
statusCode: 200,
message: "Maklumat penolakan berjaya dikemaskini.",
};
} catch (error) {
console.error(error);
return {
statusCode: 500,
message: "Gagal mengemaskini maklumat penolakan.",
};
}
});

View File

@ -119,22 +119,13 @@ export default defineEventHandler(async (event) => {
// Insert related `report` and `document` data // Insert related `report` and `document` data
for (const barang of barangList) { for (const barang of barangList) {
// await prisma.report.create({
// data: {
// permohonanID: newPermohonan.id,
// jenis_barang: barang.jenisBarangDetail,
// kuantiti_barang: barang.kuantitiBarang,
// tanda_barang: barang.tandaBarang,
// keadaan_barang: barang.keadaanBarang,
// create_by: userID,
// create_at: new Date(),
// },
// });
await prisma.report.create({ await prisma.report.create({
data: { data: {
permohonanID: newPermohonan.id, permohonanID: newPermohonan.id,
jenis_barang: barang.jenisBarangDetail, jenis_barang: barang.jenisBarangDetail,
kuantiti_barang: parseInt(barang.kuantitiBarang),
tanda_barang: barang.tandaBarang,
keadaan_barang: barang.keadaanBarang,
create_by: userID, create_by: userID,
create_at: new Date(), create_at: new Date(),
}, },

View File

@ -2,24 +2,38 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {
try { try {
const permohonan = await prisma.permohonan.findMany({ const permohonan = await prisma.permohonan.findMany({
where: {
status_permohonan: {
notIn: ["Permohonan Ditolak"],
},
},
select: { select: {
id: true, id: true,
no_siri: true, no_siri: true,
create_at: true, create_at: true,
status_permohonan: true, status_permohonan: true,
}, },
orderBy: {
create_at: "desc",
},
}); });
return { return {
statusCode: 200, statusCode: 200,
message: "Success", message: "Success",
data: permohonan.map((item, index) => ({ data: permohonan.map((item, index) => {
// Convert UTC to GMT+8
const gmt8Date = new Date(
item.create_at.getTime() + 8 * 60 * 60 * 1000
);
return {
no: index + 1, no: index + 1,
noSiri: item.no_siri, noSiri: item.no_siri,
tarikhMasa: item.create_at.toISOString().replace("T", " ").slice(0, 19), tarikhMasa: gmt8Date.toISOString().replace("T", " ").slice(0, 19),
status: item.status_permohonan, status: item.status_permohonan,
butiran: item.id, butiran: item.id,
})), };
}),
}; };
} catch (error) { } catch (error) {
return { return {