diff --git a/navigation/index.js b/navigation/index.js index 1da6349..101ebfa 100644 --- a/navigation/index.js +++ b/navigation/index.js @@ -122,4 +122,70 @@ export default [ ], meta: {}, }, + // { + // header: "Pentadbiran", + // description: "Urus aplikasi anda", + // child: [ + // { + // title: "Konfigurasi", + // icon: "ic:outline-settings", + // child: [ + // { + // title: "Persekitaran", + // path: "/devtool/config/environment", + // }, + // ], + // }, + // { + // title: "Penyunting Menu", + // icon: "ci:menu-alt-03", + // path: "/devtool/menu-editor", + // child: [], + // }, + // { + // title: "Urus Pengguna", + // path: "/devtool/user-management", + // icon: "ph:user-circle-gear", + // child: [ + // { + // title: "Senarai Pengguna", + // path: "/devtool/user-management/user", + // icon: "", + // child: [], + // }, + // { + // title: "Senarai Peranan", + // path: "/devtool/user-management/role", + // icon: "", + // child: [], + // }, + // ], + // }, + // { + // title: "Kandungan", + // icon: "mdi:pencil-ruler", + // child: [ + // { + // title: "Penyunting", + // path: "/devtool/content-editor", + // }, + // { + // title: "Templat", + // path: "/devtool/content-editor/template", + // }, + // ], + // }, + // { + // title: "Penyunting API", + // path: "/devtool/api-editor", + // icon: "material-symbols:api-rounded", + // child: [], + // }, + // ], + // meta: { + // auth: { + // role: ["Developer"], + // }, + // }, + // }, ]; diff --git a/pages/devtool/api-editor/code/index.vue b/pages/devtool/api-editor/code/index.vue index 8989fe2..d7a2ee0 100644 --- a/pages/devtool/api-editor/code/index.vue +++ b/pages/devtool/api-editor/code/index.vue @@ -3,7 +3,7 @@ import { useThemeStore } from "~/stores/theme"; definePageMeta({ - title: "Penyunting Kod API", + title: "API Code Editor", middleware: ["auth"], requiresAuth: true, }); @@ -31,6 +31,9 @@ const linterErrorText = ref(""); const linterErrorColumn = ref(0); const linterErrorLine = ref(0); +// Add new ref for loading state +const isLinterChecking = ref(false); + // Get all themes const themes = codemirrorThemes(); @@ -63,8 +66,8 @@ if (data.value.statusCode === 200) { } else { $swal .fire({ - title: "Ralat", - text: "API yang anda cuba sunting tidak dijumpai. Sila pilih API untuk disunting.", + title: "Error", + text: "The API you are trying to edit is not found. Please choose a API to edit.", icon: "error", confirmButtonText: "Ok", }) @@ -92,25 +95,30 @@ async function formatCode() { } async function checkLinterVue() { - // Call API to get the code - const { data } = await useFetch("/api/devtool/api/linter", { - initialCache: false, - method: "POST", - body: JSON.stringify({ - code: fileCode.value, - }), - }); + isLinterChecking.value = true; + try { + // Call API to get the code + const { data } = await useFetch("/api/devtool/api/linter", { + initialCache: false, + method: "POST", + body: JSON.stringify({ + code: fileCode.value, + }), + }); - if (data.value.statusCode === 200) { - linterError.value = false; - linterErrorText.value = ""; - linterErrorColumn.value = 0; - linterErrorLine.value = 0; - } else if (data.value.statusCode === 400) { - linterError.value = true; - linterErrorText.value = data.value.data.message; - linterErrorColumn.value = data.value.data.column; - linterErrorLine.value = data.value.data.line; + if (data.value.statusCode === 200) { + linterError.value = false; + linterErrorText.value = ""; + linterErrorColumn.value = 0; + linterErrorLine.value = 0; + } else if (data.value.statusCode === 400) { + linterError.value = true; + linterErrorText.value = data.value.data.message; + linterErrorColumn.value = data.value.data.column; + linterErrorLine.value = data.value.data.line; + } + } finally { + isLinterChecking.value = false; } } @@ -134,8 +142,8 @@ const saveCode = async () => { if (linterError.value) { $swal.fire({ - title: "Ralat", - text: "Terdapat ralat dalam kod anda. Sila betulkannya sebelum menyimpan.", + title: "Error", + text: "There is an error in your code. Please fix it before saving.", icon: "error", confirmButtonText: "Ok", }); @@ -153,8 +161,8 @@ const saveCode = async () => { }); if (data.value.statusCode === 200) { $swal.fire({ - title: "Berjaya", - text: "Kod telah berjaya disimpan.", + title: "Success", + text: "The code has been saved successfully.", icon: "success", confirmButtonText: "Ok", timer: 1000, @@ -170,40 +178,49 @@ const saveCode = async () => {
- {{ + {{ error }} - +
- - - Format Kod - - - Simpan API +
+ + + {{ isLinterChecking ? "Checking..." : "Save API" }} +
- +
-
Ralat ESLint
+
ESLint Error
{{ linterErrorText }}
- Baris: {{ linterErrorLine }} Lajur: {{ linterErrorColumn }} + Line: {{ linterErrorLine }} Column: {{ linterErrorColumn }}
@@ -216,7 +233,7 @@ const saveCode = async () => { mode="javascript" />
- +
diff --git a/pages/devtool/api-editor/index.vue b/pages/devtool/api-editor/index.vue index 351fc62..e632c55 100644 --- a/pages/devtool/api-editor/index.vue +++ b/pages/devtool/api-editor/index.vue @@ -23,35 +23,34 @@ const showModalEditForm = ref({ const openModalAdd = () => { showModalAddForm.value = { apiURL: "", + method: "all", }; showModalAdd.value = true; }; -const openModalEdit = (url) => { +const openModalEdit = (url, method = "all") => { const apiURL = url.replace("/api/", ""); showModalEditForm.value = { apiURL: apiURL, oldApiURL: apiURL, + method: method, }; showModalEdit.value = true; }; -// Get api list from api folder -const getApiList = async () => { - const { data } = await useFetch("/api/devtool/api/list", { - initialCache: false, - }); - return data; -}; - -const apiList = await getApiList(); +const { data: apiList, refresh } = await useFetch("/api/devtool/api/list"); const searchApi = () => { + if (!apiList.value || !apiList.value.data) return []; + return apiList.value.data.filter((api) => { - return api.name.toLowerCase().includes(searchText.value.toLowerCase()); + return ( + api.name.toLowerCase().includes(searchText.value.toLowerCase()) || + api.url.toLowerCase().includes(searchText.value.toLowerCase()) + ); }); }; @@ -78,17 +77,15 @@ const saveAddAPI = async () => { if (data.value.statusCode === 200) { nuxtApp.$swal.fire({ - title: "Berjaya", - text: "Kod telah berjaya disimpan.", + title: "Success", + text: "The code has been saved successfully.", icon: "success", confirmButtonText: "Ok", timer: 1000, }); - setTimeout(() => { - nuxtApp.$router.go( - `/devtool/api-editor/code?path=/api${data.value.data.path}` - ); - }, 1000); + // Close modal and refresh list + showModalAdd.value = false; + refresh(); } }; @@ -105,31 +102,28 @@ const saveEditAPI = async () => { if (data.value.statusCode === 200) { nuxtApp.$swal.fire({ - title: "Berjaya", - text: "Kod telah berjaya disimpan.", + title: "Success", + text: "The code has been saved successfully.", icon: "success", confirmButtonText: "Ok", timer: 1000, }); - setTimeout(() => { - nuxtApp.$router.go( - `/devtool/api-editor/code?path=/api/${showModalEditForm.value.apiURL}` - ); - }, 1000); + // Close modal and refresh list + showModalEdit.value = false; + refresh(); } }; const deleteAPI = async (apiURL) => { nuxtApp.$swal .fire({ - title: "Adakah anda pasti untuk memadam API ini?", - text: "Anda tidak akan dapat memulihkan ini!", + title: "Are you sure to delete this API?", + text: "You won't be able to revert this!", icon: "warning", showCancelButton: true, confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", - confirmButtonText: "Ya, padamkan!", - cancelButtonText: "Batal", + confirmButtonText: "Yes, delete it!", }) .then(async (result) => { if (result.isConfirmed) { @@ -144,15 +138,14 @@ const deleteAPI = async (apiURL) => { if (data.value.statusCode === 200) { nuxtApp.$swal.fire({ - title: "Berjaya", - text: "Kod telah berjaya disimpan.", + title: "Success", + text: "The code has been saved successfully.", icon: "success", confirmButtonText: "Ok", timer: 1000, }); - setTimeout(() => { - nuxtApp.$router.go(); - }, 1000); + // Refresh list after deletion + refresh(); } } }); @@ -165,13 +158,13 @@ const deleteAPI = async (apiURL) => {
@@ -181,14 +174,14 @@ const deleteAPI = async (apiURL) => {
- Tambah API + Add API
@@ -215,7 +208,7 @@ const deleteAPI = async (apiURL) => { name="material-symbols:code-blocks-outline-rounded" class="mr-2" /> - Editor Kod + Code Editor
@@ -233,34 +226,102 @@ const deleteAPI = async (apiURL) => {
- - - + - - - +
diff --git a/pages/devtool/code-playground/index.js b/pages/devtool/code-playground/index.js new file mode 100644 index 0000000..fc98745 --- /dev/null +++ b/pages/devtool/code-playground/index.js @@ -0,0 +1,35 @@ +import RsAlert from "../../../components/RsAlert.vue"; +import RsBadge from "../../../components/RsBadge.vue"; +import RsButton from "../../../components/RsButton.vue"; +import RsCard from "../../../components/RsCard.vue"; +import RsCodeMirror from "../../../components/RsCodeMirror.vue"; +import RsCollapse from "../../../components/RsCollapse.vue"; +import RsCollapseItem from "../../../components/RsCollapseItem.vue"; +import RsDropdown from "../../../components/RsDropdown.vue"; +import RsDropdownItem from "../../../components/RsDropdownItem.vue"; +import RsFieldset from "../../../components/RsFieldset.vue"; +import RsModal from "../../../components/RsModal.vue"; +import RsProgressBar from "../../../components/RsProgressBar.vue"; +import RsTab from "../../../components/RsTab.vue"; +import RsTabItem from "../../../components/RsTabItem.vue"; +import RsTable from "../../../components/RsTable.vue"; +import RsWizard from "../../../components/RsWizard.vue"; + +export { + RsAlert, + RsBadge, + RsButton, + RsCard, + RsCodeMirror, + RsCollapse, + RsCollapseItem, + RsDropdown, + RsDropdownItem, + RsFieldset, + RsModal, + RsProgressBar, + RsTab, + RsTabItem, + RsTable, + RsWizard, +}; diff --git a/pages/devtool/code-playground/index.vue b/pages/devtool/code-playground/index.vue new file mode 100644 index 0000000..f31e8e2 --- /dev/null +++ b/pages/devtool/code-playground/index.vue @@ -0,0 +1,422 @@ + + + + diff --git a/pages/devtool/config/application-log/index.vue b/pages/devtool/config/application-log/index.vue index eeb3421..677f24b 100644 --- a/pages/devtool/config/application-log/index.vue +++ b/pages/devtool/config/application-log/index.vue @@ -1,11 +1,7 @@ - - + + + + diff --git a/pages/devtool/content-editor/code/index.vue b/pages/devtool/content-editor/code/index.vue index ee47da3..223aea6 100644 --- a/pages/devtool/content-editor/code/index.vue +++ b/pages/devtool/content-editor/code/index.vue @@ -1,6 +1,6 @@ diff --git a/pages/devtool/menu-editor/index.vue b/pages/devtool/menu-editor/index.vue index 7767a17..9666eee 100644 --- a/pages/devtool/menu-editor/index.vue +++ b/pages/devtool/menu-editor/index.vue @@ -14,7 +14,7 @@ const router = useRouter(); const getRoutes = router.getRoutes(); const getNavigation = Menu ? ref(Menu) : ref([]); -const allMenus = []; +const allMenus = reactive([]); const showCode = ref(false); let i = 1; @@ -43,6 +43,16 @@ const showModalAddForm = ref({ path: "", }); +const systemPages = [ + "/devtool", + "/dashboard", + "/login", + "/logout", + "/register", + "/reset-password", + "/forgot-password", +]; + const kebabtoTitle = (str) => { if (!str) return str; return str @@ -116,6 +126,10 @@ const openModalEdit = (menu) => { }; const saveEditMenu = async () => { + // Clean the name and title ensure not spacing at the beginning or end + showModalEditForm.value.title = showModalEditForm.value.title.trim(); + showModalEditForm.value.name = showModalEditForm.value.name.trim(); + const res = await useFetch("/api/devtool/menu/edit", { method: "POST", initialCache: false, @@ -133,6 +147,8 @@ const saveEditMenu = async () => { const data = res.data.value; if (data.statusCode === 200) { + showModalEdit.value = false; + nuxtApp.$swal.fire({ title: "Success", text: data.message, @@ -140,8 +156,8 @@ const saveEditMenu = async () => { timer: 2000, showConfirmButton: false, }); - // refresh the page - nuxtApp.$router.go(); + + window.location.reload(); } }; @@ -154,6 +170,10 @@ const openModalAdd = () => { }; const saveAddMenu = async () => { + // Clean the name and title ensure not spacing at the beginning or end + showModalAddForm.value.title = showModalAddForm.value.title.trim(); + showModalAddForm.value.name = showModalAddForm.value.name.trim(); + const res = await useFetch("/api/devtool/menu/add", { method: "POST", initialCache: false, @@ -170,6 +190,8 @@ const saveAddMenu = async () => { const data = res.data.value; if (data.statusCode === 200) { + showModalAdd.value = false; + nuxtApp.$swal.fire({ title: "Success", text: data.message, @@ -177,8 +199,8 @@ const saveAddMenu = async () => { timer: 2000, showConfirmButton: false, }); - // refresh the page - nuxtApp.$router.go(); + + window.location.reload(); } else { nuxtApp.$swal.fire({ title: "Error", @@ -222,8 +244,7 @@ const deleteMenu = async (menu) => { showConfirmButton: false, }); - // refresh the page - nuxtApp.$router.go(); + window.location.reload(); } } }); @@ -319,9 +340,6 @@ const overwriteJsonFileLocal = async (menus) => { timer: 2000, showConfirmButton: false, }); - - // refresh the page - nuxtApp.$router.go(); } }; @@ -398,6 +416,21 @@ const addMenuFromList = () => { //----------------------------------------------------------------------------- //-------------------------SECOND CHILD TAB ITEM (END)------------------------- //----------------------------------------------------------------------------- + +// Add this watcher after the showModalEditForm ref declaration +watch( + () => showModalEditForm.value, + (newTitle) => { + showModalEditForm.value.name = newTitle.toLowerCase().replace(/\s+/g, "-"); + } +); + +watch( + () => showModalAddForm.value.title, + (newTitle) => { + showModalAddForm.value.name = newTitle.toLowerCase().replace(/\s+/g, "-"); + } +); diff --git a/pages/devtool/orm/index.vue b/pages/devtool/orm/index.vue new file mode 100644 index 0000000..7ab6ae1 --- /dev/null +++ b/pages/devtool/orm/index.vue @@ -0,0 +1,169 @@ + + + diff --git a/pages/devtool/orm/table/create/index.vue b/pages/devtool/orm/table/create/index.vue new file mode 100644 index 0000000..13bdac5 --- /dev/null +++ b/pages/devtool/orm/table/create/index.vue @@ -0,0 +1,315 @@ + + + diff --git a/pages/devtool/orm/table/modify/[table].vue b/pages/devtool/orm/table/modify/[table].vue new file mode 100644 index 0000000..b2e8855 --- /dev/null +++ b/pages/devtool/orm/table/modify/[table].vue @@ -0,0 +1,360 @@ + + + diff --git a/pages/devtool/orm/view/[table]/index.vue b/pages/devtool/orm/view/[table]/index.vue new file mode 100644 index 0000000..6d984cc --- /dev/null +++ b/pages/devtool/orm/view/[table]/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/pages/devtool/user-management/role-list.vue b/pages/devtool/user-management/role/index.vue similarity index 85% rename from pages/devtool/user-management/role-list.vue rename to pages/devtool/user-management/role/index.vue index 6fe9f0f..74212ef 100644 --- a/pages/devtool/user-management/role-list.vue +++ b/pages/devtool/user-management/role/index.vue @@ -1,6 +1,6 @@ - - diff --git a/pages/e-library/maklumat/[noSiri]/index.vue b/pages/e-library/maklumat/[noSiri]/index.vue deleted file mode 100644 index 3c646e0..0000000 --- a/pages/e-library/maklumat/[noSiri]/index.vue +++ /dev/null @@ -1,83 +0,0 @@ - - - - - diff --git a/pages/kemaskini-daftar/kemaskini/[noSiri]/index.vue b/pages/kemaskini-daftar/kemaskini/[noSiri]/index.vue deleted file mode 100644 index a5a3c1e..0000000 --- a/pages/kemaskini-daftar/kemaskini/[noSiri]/index.vue +++ /dev/null @@ -1,305 +0,0 @@ - - - - - diff --git a/pages/kemaskini-daftar/kemaskini/batal/[noSiri]/index.vue b/pages/kemaskini-daftar/kemaskini/batal/[noSiri]/index.vue deleted file mode 100644 index c291c5d..0000000 --- a/pages/kemaskini-daftar/kemaskini/batal/[noSiri]/index.vue +++ /dev/null @@ -1,158 +0,0 @@ -@ -1,157 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/kemaskini-daftar/kemaskini/sah/[noSiri]/index.vue b/pages/kemaskini-daftar/kemaskini/sah/[noSiri]/index.vue deleted file mode 100644 index 4dfd35b..0000000 --- a/pages/kemaskini-daftar/kemaskini/sah/[noSiri]/index.vue +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pages/kemaskini-daftar/laporan/[reportID]/index.vue b/pages/kemaskini-daftar/laporan/[reportID]/index.vue deleted file mode 100644 index e6def6a..0000000 --- a/pages/kemaskini-daftar/laporan/[reportID]/index.vue +++ /dev/null @@ -1,345 +0,0 @@ - - - - - diff --git a/pages/kemaskini-daftar/maklumat/[noSiri]/index.vue b/pages/kemaskini-daftar/maklumat/[noSiri]/index.vue deleted file mode 100644 index 9cac5bb..0000000 --- a/pages/kemaskini-daftar/maklumat/[noSiri]/index.vue +++ /dev/null @@ -1,734 +0,0 @@ - - - - - diff --git a/pages/kemaskini-daftar/senarai/index.vue b/pages/kemaskini-daftar/senarai/index.vue deleted file mode 100644 index 50ca6c4..0000000 --- a/pages/kemaskini-daftar/senarai/index.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/pages/pengesanan-penyamaran/baru/index.vue b/pages/pengesanan-penyamaran/baru/index.vue deleted file mode 100644 index a32b9f3..0000000 --- a/pages/pengesanan-penyamaran/baru/index.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - diff --git a/pages/pengesanan-penyamaran/kemaskini/[kesID]/index.vue b/pages/pengesanan-penyamaran/kemaskini/[kesID]/index.vue deleted file mode 100644 index 9a501ab..0000000 --- a/pages/pengesanan-penyamaran/kemaskini/[kesID]/index.vue +++ /dev/null @@ -1,300 +0,0 @@ - - - - - diff --git a/pages/pengesanan-penyamaran/senarai/index.vue b/pages/pengesanan-penyamaran/senarai/index.vue deleted file mode 100644 index 9fcf442..0000000 --- a/pages/pengesanan-penyamaran/senarai/index.vue +++ /dev/null @@ -1,152 +0,0 @@ - - - diff --git a/pages/permohonan-temujanji/baru/index.vue b/pages/permohonan-temujanji/baru/index.vue deleted file mode 100644 index 90e2f06..0000000 --- a/pages/permohonan-temujanji/baru/index.vue +++ /dev/null @@ -1,520 +0,0 @@ - - - - - diff --git a/pages/permohonan-temujanji/kemaskini/[noSiri]/index.vue b/pages/permohonan-temujanji/kemaskini/[noSiri]/index.vue deleted file mode 100644 index 9b27559..0000000 --- a/pages/permohonan-temujanji/kemaskini/[noSiri]/index.vue +++ /dev/null @@ -1,557 +0,0 @@ - - - - - diff --git a/pages/permohonan-temujanji/senarai/index.vue b/pages/permohonan-temujanji/senarai/index.vue deleted file mode 100644 index 19c8dab..0000000 --- a/pages/permohonan-temujanji/senarai/index.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - diff --git a/pages/prototype/for-01/kemaskini-daftar/index.vue b/pages/prototype/for-01/kemaskini-daftar/index.vue deleted file mode 100644 index 055a17c..0000000 --- a/pages/prototype/for-01/kemaskini-daftar/index.vue +++ /dev/null @@ -1,233 +0,0 @@ - - - diff --git a/pages/prototype/for-01/permohonan/index.vue b/pages/prototype/for-01/permohonan/index.vue deleted file mode 100644 index e7ebeb7..0000000 --- a/pages/prototype/for-01/permohonan/index.vue +++ /dev/null @@ -1,334 +0,0 @@ - - - diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0560b9e..3cfc6be 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -16,168 +16,16 @@ model audit { auditCreatedDate DateTime? @db.DateTime(0) } -model document { - documentID Int @id @default(autoincrement()) - userID Int? - documentName String? @db.VarChar(255) - documentURL String? @db.VarChar(255) - documentType String? @db.VarChar(255) - documentExtension String? @db.VarChar(255) - imageMIMEType String? @db.VarChar(255) - documentSize Int? - documentStatus String? @default("ACTIVE") @db.VarChar(255) - documentCreatedDate String? @db.VarChar(255) - documentModifiedDate String? @db.VarChar(255) - user user? @relation(fields: [userID], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "document_ibfk_1") - permohonan_forensik_checking permohonan_forensik_checking[] - report report[] - report_doc_support report_doc_support[] - temujanji_temujanji_gambarSubjekTodocument temujanji[] @relation("temujanji_gambarSubjekTodocument") - temujanji_temujanji_gambarCapJariTodocument temujanji[] @relation("temujanji_gambarCapJariTodocument") - temujanji_detail temujanji_detail[] - temujanji_log temujanji_log[] - - @@index([userID], map: "userID") -} - model lookup { - lookupID Int @id @default(autoincrement()) - lookupOrder Int? - lookupTitle String? @db.VarChar(255) - lookupRefCode String? @db.VarChar(255) - lookupValue String? @db.VarChar(255) - lookupType String? @db.VarChar(255) - lookupStatus String? @db.VarChar(255) - lookupCreatedDate DateTime? @db.DateTime(0) - lookupModifiedDate DateTime? @db.DateTime(0) - permohonan permohonan[] - permohonan_jenis_barang permohonan_jenis_barang[] - permohonan_penolakan permohonan_penolakan[] - report_report_dapatanTolookup report[] @relation("report_dapatanTolookup") - report_report_jenis_barangTolookup report[] @relation("report_jenis_barangTolookup") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model notifikasi { - notifikasiID Int @id @default(autoincrement()) - penghantarID Int - penerimaID Int - body String @db.Text - status Int -} - -model pemohon { - id Int @id @default(autoincrement()) - userID Int - pangkat_pemohon String @db.VarChar(255) - no_pegawai_pemohon String @db.VarChar(255) - user user @relation(fields: [userID], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "pemohon_ibfk_1") - permohonan permohonan[] - temujanji temujanji[] - - @@index([userID], map: "userID") -} - -model penghantar { - id Int @id @default(autoincrement()) - nama_penghantar String @db.VarChar(255) - pangkat_penghantar String @db.VarChar(255) - no_pegawai_penghantar String @db.VarChar(255) - permohonan permohonan[] -} - -model permohonan { - id Int @id @default(autoincrement()) - no_siri String @unique(map: "Permohonan_no_siri_key") @db.VarChar(255) - pemohonID Int? - penghantar_sama_dengan_pemohon Int? - penghantarID Int? - status_permohonan String @db.VarChar(255) - ringkasan_kenyataan_kes String? @db.Text - bilangan Int? - jenis_barang Int? - tanda_barang String? @db.VarChar(255) - keadaan_barang String? @db.VarChar(255) - kuantiti_barang Int? - jenis_barang_details String? @db.Text - no_laporan_polis String? @db.VarChar(255) - no_kertas_siasatan String? @db.VarChar(255) - tarikh_temujanji DateTime? @db.DateTime(0) - slot_masa DateTime? @db.Time(0) - create_at DateTime? @db.DateTime(0) - modified_at DateTime? @db.DateTime(0) - lookup lookup? @relation(fields: [jenis_barang], references: [lookupID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_ibfk_3") - pemohon pemohon? @relation(fields: [pemohonID], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "permohonan_ibfk_2") - penghantar penghantar? @relation(fields: [penghantarID], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "permohonan_ibfk_1") - permohonan_assign_forensik permohonan_assign_forensik[] - permohonan_jenis_barang permohonan_jenis_barang[] - permohonan_penerimaan permohonan_penerimaan? - permohonan_semakan permohonan_semakan? - report report[] - - @@index([pemohonID], map: "idx_pemohon") - @@index([penghantarID], map: "idx_penghantar") - @@index([jenis_barang], map: "jenis_barang") -} - -model permohonan_assign_forensik { - assignID Int @id @default(autoincrement()) - permohonanID Int - pegawai_forensikID Int - permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_assign_forensik_ibfk_2") - user user @relation(fields: [pegawai_forensikID], references: [userID], onUpdate: Restrict, map: "permohonan_assign_forensik_ibfk_3") - permohonan_forensik_checking permohonan_forensik_checking[] - - @@index([pegawai_forensikID], map: "pegawai_forensikID") - @@index([permohonanID], map: "permohonanID") -} - -model permohonan_penerimaan { - penerimaanID Int @id @default(autoincrement()) - permohonanID Int @unique(map: "permohonanID") - peralatan_keadaan_baik Int - pegawai_berkelayakan Int - kaedah_dpt_dilakukan Int - subkontrak_diperlukan Int - tugasan_diterima Int - ulasan_pegawai String? @db.Text - create_at DateTime @db.DateTime(0) - diterima_oleh Int - 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") - - @@index([diterima_oleh], map: "diterima_oleh") -} - -model permohonan_penolakan { - penolakanID Int @id @default(autoincrement()) - permohonanID Int @unique(map: "permohonanID") - sebab_penolakan Int - lain_sebab String? @db.VarChar(255) - create_at DateTime? @db.DateTime(0) - ditolak_oleh Int? - lookup lookup @relation(fields: [sebab_penolakan], references: [lookupID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penolakan_ibfk_1") - user user? @relation(fields: [ditolak_oleh], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_penolakan_ibfk_2") - - @@index([ditolak_oleh], map: "ditolak_oleh") - @@index([sebab_penolakan], map: "sebab_penolakan") -} - -model permohonan_semakan { - semakanID Int @id @default(autoincrement()) - permohonanID Int @unique(map: "permohonanID") - peralatan_keadaan_baik Int? - pegawai_berkelayakan Int? - kaedah_dpt_dilakukan Int? - subkontrak_diperlukan Int? - tugasan_diterima Int? - ulasan_pegawai String? @db.Text - create_at DateTime? @db.DateTime(0) - disemak_oleh Int? - 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") - - @@index([disemak_oleh], map: "disemak_oleh") + lookupID Int @id @default(autoincrement()) + lookupOrder Int? + lookupTitle String? @db.VarChar(255) + lookupRefCode String? @db.VarChar(255) + lookupValue String? @db.VarChar(255) + lookupType String? @db.VarChar(255) + lookupStatus String? @db.VarChar(255) + lookupCreatedDate DateTime? @db.DateTime(0) + lookupModifiedDate DateTime? @db.DateTime(0) } model role { @@ -190,30 +38,18 @@ model role { userrole userrole[] } -model status { - statusID Int @id @default(autoincrement()) - status_name String @db.VarChar(255) -} - model user { - userID Int @id @default(autoincrement()) - userSecretKey String? @db.VarChar(255) - userUsername String? @db.VarChar(255) - userPassword String? @db.VarChar(255) - userFullName String? @db.VarChar(255) - userEmail String? @db.VarChar(255) - userPhone String? @db.VarChar(255) - userStatus String? @db.VarChar(255) - userCreatedDate DateTime? @db.DateTime(0) - userModifiedDate DateTime? @db.DateTime(0) - document document[] - pemohon pemohon[] - permohonan_approval permohonan_approval[] - permohonan_assign_forensik permohonan_assign_forensik[] - permohonan_penerimaan permohonan_penerimaan[] - permohonan_penolakan permohonan_penolakan[] - permohonan_semakan permohonan_semakan[] - userrole userrole[] + userID Int @id @default(autoincrement()) + userSecretKey String? @db.VarChar(255) + userUsername String? @db.VarChar(255) + userPassword String? @db.VarChar(255) + userFullName String? @db.VarChar(255) + userEmail String? @db.VarChar(255) + userPhone String? @db.VarChar(255) + userStatus String? @db.VarChar(255) + userCreatedDate DateTime? @db.DateTime(0) + userModifiedDate DateTime? @db.DateTime(0) + userrole userrole[] } model userrole { @@ -228,191 +64,14 @@ model userrole { @@index([userRoleUserID], map: "FK_userrole_user") } -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model permohonan_approval { - approvalID Int @id @default(autoincrement()) - permohonanID Int - approve_by Int - approval_status Int - ulasan String? @db.Text - approval_date DateTime @db.DateTime(0) - user user @relation(fields: [approve_by], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_approval_ibfk_1") - - @@index([approve_by], map: "userID") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model permohonan_forensik_checking { - checkingID Int @id @default(autoincrement()) - assignID Int - gambar Int? - ulasan String? @db.Text - dapatan String @db.VarChar(255) - create_at DateTime @db.DateTime(0) - permohonan_assign_forensik permohonan_assign_forensik @relation(fields: [assignID], references: [assignID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_forensik_checking_ibfk_1") - document document? @relation(fields: [gambar], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_forensik_checking_ibfk_2") - - @@index([assignID], map: "assignID") - @@index([gambar], map: "gambar") -} - -model permohonan_jenis_barang { - barangID Int @id @default(autoincrement()) - permohonanID Int - jenis_barang Int - barang_status String @default("ACTIVE") @db.VarChar(255) - lookup lookup @relation(fields: [jenis_barang], references: [lookupID], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_jenis_barang_ibfk_1") - permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "permohonan_jenis_barang_ibfk_2") - - @@index([jenis_barang], map: "jenis_barang") - @@index([permohonanID], map: "permohonanID") -} - -model permohonan_tanda_barang { - tandaID Int @id @default(autoincrement()) - tanda_name String @db.VarChar(255) - tanda_status String @default("ACTIVE") @db.VarChar(255) -} - -model report { - reportID Int @id @default(autoincrement()) - permohonanID Int - jenis_barang Int - tanda_barang String? @db.VarChar(255) - keadaan_barang String? @db.VarChar(255) - kuantiti_barang Int? - peralatan String? @db.VarChar(255) - langkah_langkah String? @db.VarChar(255) - gambarID Int? - ulasan String? @db.Text - dapatan Int? - create_at DateTime? @db.DateTime(0) - create_by Int - document document? @relation(fields: [gambarID], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "report_ibfk_2") - lookup_report_dapatanTolookup lookup? @relation("report_dapatanTolookup", fields: [dapatan], references: [lookupID], onDelete: NoAction, onUpdate: NoAction, map: "report_ibfk_3") - lookup_report_jenis_barangTolookup lookup @relation("report_jenis_barangTolookup", fields: [jenis_barang], references: [lookupID], onDelete: NoAction, onUpdate: NoAction, map: "report_ibfk_4") - permohonan permohonan @relation(fields: [permohonanID], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "report_ibfk_1") - report_doc_support report_doc_support[] - - @@index([dapatan], map: "dapatan") - @@index([gambarID], map: "gambarID") - @@index([jenis_barang], map: "jenis_barang") - @@index([permohonanID], map: "permohonanID") -} - -model report_doc_support { - report_attachID Int @id @default(autoincrement()) - reportID Int - documentID Int - document document @relation(fields: [documentID], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "report_doc_support_ibfk_1") - report report @relation(fields: [reportID], references: [reportID], onDelete: NoAction, onUpdate: NoAction, map: "report_doc_support_ibfk_2") - - @@index([documentID], map: "documentID") - @@index([reportID], map: "reportID") -} - -model temujanji { - temujanjiID Int @id @default(autoincrement()) - noSiri String @db.VarChar(255) - temujanjiDetailID Int? - pemohonID Int - jenisSemakan String @db.VarChar(255) - tarikh DateTime @db.Date - masa DateTime @db.Time(0) - status String? @db.VarChar(255) - gambarSubjek Int? - gambarCapJari Int? - create_at DateTime? @default(now()) @db.DateTime(0) - modified_at DateTime? @db.DateTime(0) - document_temujanji_gambarSubjekTodocument document? @relation("temujanji_gambarSubjekTodocument", fields: [gambarSubjek], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "fk_gambarSubjek") - document_temujanji_gambarCapJariTodocument document? @relation("temujanji_gambarCapJariTodocument", fields: [gambarCapJari], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "fk_gambarCapJari") - pemohon pemohon @relation(fields: [pemohonID], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "temujanji_ibfk_1") - temujanji_detail temujanji_detail? @relation(fields: [temujanjiDetailID], references: [temujanjiDetailID], onDelete: NoAction, onUpdate: NoAction, map: "temujanji_ibfk_4") - temujanji_log temujanji_log[] - - @@index([gambarCapJari], map: "idx_gambarCapJari") - @@index([gambarSubjek], map: "idx_gambarSubjek") - @@index([pemohonID], map: "idx_pemohonID") - @@index([temujanjiDetailID], map: "temujanjiDetailID") -} - -model temujanji_detail { - temujanjiDetailID Int @id @default(autoincrement()) - negara String? @db.VarChar(255) - namaPemilik String? @db.VarChar(255) - noDokumen String? @db.VarChar(255) - kewarganegaraan String? @db.VarChar(255) - tarikhLahir DateTime? @db.Date - jantina String? @db.VarChar(255) - tarikhLuputDokumen DateTime? @db.Date - skorPersamaanMuka Decimal? @db.Decimal(10, 2) - skorPersamaanCapJari Decimal? @db.Decimal(10, 2) - umur Int? - tinggi Decimal? @db.Decimal(10, 2) - warnaRambut String? @db.VarChar(255) - bangsa String? @db.VarChar(255) - etnik String? @db.VarChar(255) - bentukKepala String? @db.VarChar(255) - mata String? @db.VarChar(255) - telinga String? @db.VarChar(255) - hidung String? @db.VarChar(255) - mulut String? @db.VarChar(255) - parut String? @db.VarChar(255) - sejarahPerjalanan String? @db.VarChar(255) - persamaanTandaTangan String? @db.VarChar(255) - pemeriksaanLain String? @db.VarChar(255) - dapatan String? @db.VarChar(255) - laporanSystemTdb Int? - create_at DateTime? @db.DateTime(0) - modified_at DateTime? @db.DateTime(0) - temujanji temujanji[] - document document? @relation(fields: [laporanSystemTdb], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "temujanji_detail_ibfk_2") - - @@index([laporanSystemTdb], map: "idx_laporanSystemTdb") -} - -model temujanji_log { - temujanjiLogID Int @id @default(autoincrement()) - temujanjiID Int - pemohonID Int? - jenisSemakan String? @db.VarChar(255) - tarikh DateTime? @db.Date - masa DateTime? @db.Time(0) - gambarSubjek Int? - gambarCapJari Int? - negara String? @db.VarChar(255) - namaPemilik String? @db.VarChar(255) - noDokumen String? @db.VarChar(255) - kewarganegaraan String? @db.VarChar(255) - tarikhLahir DateTime? @db.Date - jantina String? @db.VarChar(255) - tarikhLuputDokumen DateTime? @db.Date - skorPersamaanMuka Decimal? @db.Decimal(10, 2) - skorPersamaanCapJari Decimal? @db.Decimal(10, 2) - umur Int? - tinggi Decimal? @db.Decimal(10, 2) - warnaRambut String? @db.VarChar(255) - bangsa String? @db.VarChar(255) - etnik String? @db.VarChar(255) - bentukKepala String? @db.VarChar(255) - mata String? @db.VarChar(255) - telinga String? @db.VarChar(255) - hidung String? @db.VarChar(255) - mulut String? @db.VarChar(255) - parut String? @db.VarChar(255) - sejarahPerjalanan String? @db.VarChar(255) - persamaanTandaTangan String? @db.VarChar(255) - pemeriksaanLain String? @db.VarChar(255) - dapatan String? @db.VarChar(255) - laporanSystemTdb Int? - create_at DateTime? @db.DateTime(0) - modified_at DateTime? @db.DateTime(0) - document document? @relation(fields: [laporanSystemTdb], references: [documentID], onDelete: NoAction, onUpdate: NoAction, map: "temujanji_log_ibfk_1") - temujanji temujanji @relation(fields: [temujanjiID], references: [temujanjiID], onDelete: NoAction, onUpdate: NoAction, map: "temujanji_log_ibfk_2") - - @@index([gambarCapJari], map: "gambarCapJari") - @@index([gambarSubjek], map: "gambarSubjek") - @@index([laporanSystemTdb], map: "laporanSystemTdb") - @@index([pemohonID], map: "pemohonID") - @@index([temujanjiID], map: "temujanjiID") +model customer { + cust_id Int @id @default(autoincrement()) + cust_name String? @db.VarChar(255) + cust_username String? @db.VarChar(255) + cust_ic_number String? @db.VarChar(255) + cust_address String? @db.VarChar(255) + cust_dob DateTime? @db.Date + cust_gender String? @db.VarChar(255) + cust_status Int? + cust_created_datetime DateTime? @db.DateTime(0) } diff --git a/server/api/devtool/api/linter.js b/server/api/devtool/api/linter.js index 3c2f35d..32572eb 100644 --- a/server/api/devtool/api/linter.js +++ b/server/api/devtool/api/linter.js @@ -14,40 +14,170 @@ export default defineEventHandler(async (event) => { // run linter const code = body.code; + const validateNitroCode = (code) => { + // Check if this is a server route file + const isServerRoute = code.includes("defineEventHandler"); - // enable babel-eslint parser and requireConfigFile: false - // ignore code export default defineEventHandler((event) => { + if (isServerRoute) { + let lineNumber = 1; - const eslint = new ESLint({ - overrideConfig: { - parser: "@babel/eslint-parser", - extends: ["@kiwicom"], - parserOptions: { - requireConfigFile: false, - ecmaVersion: 2020, - sourceType: "module", + // 1. Validate event handler structure + if (!code.includes("export default defineEventHandler")) { + throw { + message: + "Nitro route handlers must use 'export default defineEventHandler'", + line: 1, + column: 0, + }; + } + + // 2. Check for proper request handling + const hasRequestBody = code.includes("await readBody(event)"); + const hasRequestQuery = code.includes("getQuery(event)"); + const usesEventWithoutImport = + code.includes("event.") && !hasRequestBody && !hasRequestQuery; + + if (usesEventWithoutImport) { + // Find the line where event is improperly used + const lines = code.split("\n"); + for (let i = 0; i < lines.length; i++) { + if ( + lines[i].includes("event.") && + !lines[i].includes("readBody") && + !lines[i].includes("getQuery") + ) { + throw { + message: + "Use 'readBody(event)' for POST data or 'getQuery(event)' for query parameters", + line: i + 1, + column: lines[i].indexOf("event."), + }; + } + } + } + + // 3. Validate response structure + const responseRegex = /return\s+{([^}]+)}/g; + let match; + let lastIndex = 0; + + while ((match = responseRegex.exec(code)) !== null) { + lineNumber += (code.slice(lastIndex, match.index).match(/\n/g) || []) + .length; + lastIndex = match.index; + + const responseContent = match[1]; + + // Check for required response properties + if (!responseContent.includes("statusCode")) { + throw { + message: "API responses must include a 'statusCode' property", + line: lineNumber, + column: match.index - code.lastIndexOf("\n", match.index), + }; + } + + // Validate status code usage + const statusMatch = responseContent.match(/statusCode:\s*(\d+)/); + if (statusMatch) { + const statusCode = parseInt(statusMatch[1]); + if (![200, 201, 400, 401, 403, 404, 500].includes(statusCode)) { + throw { + message: `Invalid status code: ${statusCode}. Use standard HTTP status codes.`, + line: lineNumber, + column: statusMatch.index, + }; + } + } + } + + // 4. Check error handling + if (code.includes("try") && !code.includes("catch")) { + throw { + message: + "Missing error handling. Add a catch block for try statements.", + line: + code.split("\n").findIndex((line) => line.includes("try")) + 1, + column: 0, + }; + } + + // 5. Validate async/await usage + const asyncLines = code.match(/async.*=>/g) || []; + const awaitLines = code.match(/await\s+/g) || []; + + if (awaitLines.length > 0 && asyncLines.length === 0) { + throw { + message: "Using 'await' requires an async function", + line: + code.split("\n").findIndex((line) => line.includes("await")) + 1, + column: 0, + }; + } + + // // 6. Check for proper imports + // const requiredImports = new Set(); + // if (hasRequestBody) requiredImports.add("readBody"); + // if (hasRequestQuery) requiredImports.add("getQuery"); + + // const importLines = code.match(/import.*from/g) || []; + // requiredImports.forEach((imp) => { + // if (!importLines.some((line) => line.includes(imp))) { + // throw { + // message: `Missing import for '${imp}' utility`, + // line: 1, + // column: 0, + // }; + // } + // }); + } + }; + + try { + validateNitroCode(code); + + const eslint = new ESLint({ + overrideConfig: { + parser: "@babel/eslint-parser", + extends: ["@kiwicom"], + parserOptions: { + requireConfigFile: false, + ecmaVersion: 2020, + sourceType: "module", + }, }, - }, - useEslintrc: false, - }); + useEslintrc: false, + }); - const results = await eslint.lintText(code); + const results = await eslint.lintText(code); - if (results[0].messages.length > 0) { - const messages = results[0].messages[0]; + if (results[0].messages.length > 0) { + const messages = results[0].messages[0]; + + if (messages.fatal === true) { + return { + statusCode: 400, + message: "Bad Linter Test", + data: messages, + }; + } - if (messages.fatal === true) { return { - statusCode: 400, - message: "Bad Linter Test", + statusCode: 200, + message: "Good Linter test", data: messages, }; } - + } catch (error) { + console.log(error); return { - statusCode: 200, - message: "Good Linter test", - data: messages, + statusCode: 400, + message: "Bad Linter Test", + data: { + message: error.message, + line: error.line || 1, + column: error.column || 0, + }, }; } } catch (error) { diff --git a/server/api/devtool/api/save.js b/server/api/devtool/api/save.js index 0991e28..567ff59 100644 --- a/server/api/devtool/api/save.js +++ b/server/api/devtool/api/save.js @@ -8,8 +8,8 @@ export default defineEventHandler(async (event) => { const codeDefault = ` export default defineEventHandler(async (event) => { - // const query = await getQuery(event); || Get Params from URL - // const body = await readBody(event); || Get Body Data + // const query = await getQuery(event); // Get Params from URL + // const body = await readBody(event); // Get Body Data return { statusCode: 200, diff --git a/server/api/devtool/content/code/linter.js b/server/api/devtool/content/code/linter.js index 01a52d4..60399ad 100644 --- a/server/api/devtool/content/code/linter.js +++ b/server/api/devtool/content/code/linter.js @@ -1,4 +1,3 @@ -// import esline vue import { ESLint } from "eslint"; export default defineEventHandler(async (event) => { @@ -12,9 +11,366 @@ export default defineEventHandler(async (event) => { }; } - // run linter const code = body.code; + // Extract script and template content once + const scriptContent = + code.match(/]*>([\s\S]*?)<\/script>/)?.[1] || ""; + const templateContent = code.match(/