From 13f3c24c7b789438a46ffc5d4420d14745689386 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sat, 1 Feb 2025 07:04:15 +0700
Subject: [PATCH 1/8] feat: validate profil edit form
---
pages/profil/edit.vue | 60 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 7 deletions(-)
diff --git a/pages/profil/edit.vue b/pages/profil/edit.vue
index dc84e3b..864162d 100644
--- a/pages/profil/edit.vue
+++ b/pages/profil/edit.vue
@@ -6,6 +6,8 @@ import Toast from "primevue/toast"
import type { Pengguna } from "@/types"
import { useToast } from "primevue/usetoast"
import IconArrowLeft from "~icons/mdi/arrow-left"
+import { zodResolver } from "@primevue/forms/resolvers/zod"
+import { z } from "zod"
useHead({
title: "Edit profil",
@@ -24,9 +26,19 @@ const { data: userProfile } = await useAsyncData(async () => {
return false
})
+const formSchema = z.object({
+ nama: z.string().nonempty("nama kamu gak mungkin kosong."),
+ kelas: z.enum(["X", "XI", "XII"], { message: "kelasnya gak valid." }),
+ jurusan: z.string().nonempty("jurusan kamu gak mungkin kosong."),
+})
+
+const resolver = zodResolver(formSchema)
+
const toast = useToast()
-async function updateProfile() {
+async function updateProfile({ valid }: { valid: boolean }) {
+ if (!valid) return
+
try {
await authStore.handleUpdateProfile(userProfile.value as Pengguna)
@@ -58,27 +70,61 @@ async function updateProfile() {
-
+
-
+
+
From 0510bc007ae60c84e54308734533ad9b7e0e9852 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sat, 1 Feb 2025 09:50:41 +0700
Subject: [PATCH 2/8] feat: admin tambah buku wip
---
pages/admin/buku/tambah.vue | 66 +++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 29 deletions(-)
diff --git a/pages/admin/buku/tambah.vue b/pages/admin/buku/tambah.vue
index fee4cd4..8c101a1 100644
--- a/pages/admin/buku/tambah.vue
+++ b/pages/admin/buku/tambah.vue
@@ -7,6 +7,8 @@ import type { PostgrestError } from "@supabase/supabase-js"
import IconArrowLeft from "~icons/mdi/arrow-left"
import type { Database } from "~/types/database.types.ts"
import { InputText, InputNumber, FileUpload } from "primevue"
+import { zodResolver } from "@primevue/forms/resolvers/zod"
+import { z } from "zod"
useHead({
title: "Tambah Buku",
@@ -35,6 +37,23 @@ async function uploadBookImage(isbn: string, file: File) {
return error
}
+const schema = z.object({
+ judul: z.string(),
+ no_isbn: z.string(),
+ kategori_id: z.number(),
+ asal: z.string(),
+ jumlah_exspl: z
+ .number()
+ .min(0, "jumlah harus 0 atau lebih.")
+ .max(10000, "banyak amat sih ga mungkin lah bukunya segitu."),
+ penerbit: z.string(),
+ alamat_terbit: z.string(),
+ tahun_terbit: z.string().regex(/\d{0,4}/, { message: "tahun terbit harus memiliki 4 digit." }),
+ penulis: z.string(),
+})
+
+const resolver = zodResolver(schema)
+
async function addNewBook(buku: Buku) {
isLoading.value = true
@@ -137,7 +156,15 @@ const router = useRouter()
-
+
From 86e51e24d46686ddd9ccf92f05ad80cbf185ca22 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sat, 1 Feb 2025 04:50:07 +0000
Subject: [PATCH 3/8] feat: validate form in buku/tambah
---
pages/admin/buku/tambah.vue | 106 ++++++++++++++++++++++++++----------
1 file changed, 77 insertions(+), 29 deletions(-)
diff --git a/pages/admin/buku/tambah.vue b/pages/admin/buku/tambah.vue
index 8c101a1..e9ec1ac 100644
--- a/pages/admin/buku/tambah.vue
+++ b/pages/admin/buku/tambah.vue
@@ -2,13 +2,13 @@
import { useBuku, useDialog } from "@/composables"
import Select from "primevue/select"
import { StorageError } from "@supabase/storage-js"
-import type { Buku } from "@/types"
import type { PostgrestError } from "@supabase/supabase-js"
import IconArrowLeft from "~icons/mdi/arrow-left"
import type { Database } from "~/types/database.types.ts"
import { InputText, InputNumber, FileUpload } from "primevue"
import { zodResolver } from "@primevue/forms/resolvers/zod"
import { z } from "zod"
+import type { FormSubmitEvent } from "@primevue/forms"
useHead({
title: "Tambah Buku",
@@ -19,7 +19,7 @@ definePageMeta({
})
const supabase = useSupabaseClient()
-const { buku } = useBuku()
+const { buku: formData } = useBuku()
const isLoading = ref(false)
const { dialog } = useDialog()
@@ -28,38 +28,58 @@ const { dialog: errDialog } = useDialog()
const { newImage, previewURL, previewImage } = usePreviewImage()
async function uploadBookImage(isbn: string, file: File) {
- if (!buku.value) return console.trace("buku gak ada????")
+ if (!formData.value) return console.trace("buku gak ada????")
- buku.value.image = `public/${isbn}`
- const { error } = await supabase.storage.from("Buku").upload(buku.value.image, file, {
+ formData.value.image = `public/${isbn}`
+ const { error } = await supabase.storage.from("Buku").upload(formData.value.image, file, {
upsert: true,
})
return error
}
const schema = z.object({
- judul: z.string(),
- no_isbn: z.string(),
- kategori_id: z.number(),
- asal: z.string(),
+ judul: z.string().nonempty("judul tidak boleh kosong."),
+ no_isbn: z.string().nonempty("isbn tidak boleh kosong."),
+ image: z.string().nullable(),
+ kategori_id: z.number({ message: "buku harus memiliki kategori." }),
+ asal: z.string().default("-"),
jumlah_exspl: z
- .number()
+ .number({ message: "jumlah harus berupa angka." })
.min(0, "jumlah harus 0 atau lebih.")
.max(10000, "banyak amat sih ga mungkin lah bukunya segitu."),
- penerbit: z.string(),
- alamat_terbit: z.string(),
- tahun_terbit: z.string().regex(/\d{0,4}/, { message: "tahun terbit harus memiliki 4 digit." }),
- penulis: z.string(),
+ penerbit: z.string().nonempty("penerbit tidak boleh kosong."),
+ alamat_terbit: z.string().nonempty("alamat penerbit tidak boleh kosong."),
+ tahun_terbit: z
+ .string()
+ .regex(/^\d{0,4}$/, { message: "tahun terbit harus berupa angka 0 sampai 4 digit." }),
+ penulis: z.string().nonempty("penulis tidak boleh kosong"),
})
const resolver = zodResolver(schema)
-async function addNewBook(buku: Buku) {
+async function addNewBook({ valid, values }: FormSubmitEvent) {
+ if (!valid) return
+
isLoading.value = true
+ const { data: buku, success, error } = schema.safeParse(values)
+ if (!buku || !success) {
+ isLoading.value = false
+
+ console.log(buku, success, error)
+
+ return toast.add({
+ severity: "error",
+ summary: "Gagal menambahkan buku",
+ detail: "gagal menambahkan buku. Silahkan refresh atau coba lagi dalam beberapa saat.",
+ life: 10000,
+ })
+ }
+
const { no_isbn } = buku
try {
+ // upload image if admin puts an image
if (newImage.value) {
const uploadError = await uploadBookImage(no_isbn, newImage.value)
if (uploadError) throw uploadError
@@ -135,11 +155,7 @@ const router = useRouter()
-
- Ada kesalahan saat mengambil data buku. Silahkan coba lagi
-
-
-
+
@@ -149,7 +165,6 @@ const router = useRouter()
width="150"
height="800"
class="size-full object-cover aspect-auto"
- :alt="`gambar buku ${buku?.judul}`"
/>
Gambar buku akan muncul di sini.
@@ -158,20 +173,20 @@ const router = useRouter()
+
+
From 09a14e3a487da5b3c84dc6a19f0d056679b639f7 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sat, 1 Feb 2025 05:20:39 +0000
Subject: [PATCH 4/8] export to lib for reuse
---
lib/buku.ts | 22 ++++++++++++++++++++++
pages/admin/buku/tambah.vue | 23 +----------------------
2 files changed, 23 insertions(+), 22 deletions(-)
create mode 100644 lib/buku.ts
diff --git a/lib/buku.ts b/lib/buku.ts
new file mode 100644
index 0000000..59f9459
--- /dev/null
+++ b/lib/buku.ts
@@ -0,0 +1,22 @@
+import { zodResolver } from "@primevue/forms/resolvers/zod"
+import { z } from "zod"
+
+export const schema = z.object({
+ judul: z.string().nonempty("judul tidak boleh kosong."),
+ no_isbn: z.string().nonempty("isbn tidak boleh kosong."),
+ image: z.string().optional().nullable().default(null),
+ kategori_id: z.number({ message: "buku harus memiliki kategori." }),
+ asal: z.string().default("-"),
+ jumlah_exspl: z
+ .number({ message: "jumlah harus berupa angka." })
+ .min(0, "jumlah harus 0 atau lebih.")
+ .max(10000, "banyak amat sih ga mungkin lah bukunya segitu."),
+ penerbit: z.string().nonempty("penerbit tidak boleh kosong."),
+ alamat_terbit: z.string().nonempty("alamat penerbit tidak boleh kosong."),
+ tahun_terbit: z
+ .string()
+ .regex(/^\d{0,4}$/, { message: "tahun terbit harus berupa angka 0 sampai 4 digit." }),
+ penulis: z.string().nonempty("penulis tidak boleh kosong"),
+})
+
+export const resolver = zodResolver(schema)
diff --git a/pages/admin/buku/tambah.vue b/pages/admin/buku/tambah.vue
index e9ec1ac..80386b1 100644
--- a/pages/admin/buku/tambah.vue
+++ b/pages/admin/buku/tambah.vue
@@ -6,9 +6,8 @@ import type { PostgrestError } from "@supabase/supabase-js"
import IconArrowLeft from "~icons/mdi/arrow-left"
import type { Database } from "~/types/database.types.ts"
import { InputText, InputNumber, FileUpload } from "primevue"
-import { zodResolver } from "@primevue/forms/resolvers/zod"
-import { z } from "zod"
import type { FormSubmitEvent } from "@primevue/forms"
+import { schema, resolver } from "~/lib/buku"
useHead({
title: "Tambah Buku",
@@ -37,26 +36,6 @@ async function uploadBookImage(isbn: string, file: File) {
return error
}
-const schema = z.object({
- judul: z.string().nonempty("judul tidak boleh kosong."),
- no_isbn: z.string().nonempty("isbn tidak boleh kosong."),
- image: z.string().nullable(),
- kategori_id: z.number({ message: "buku harus memiliki kategori." }),
- asal: z.string().default("-"),
- jumlah_exspl: z
- .number({ message: "jumlah harus berupa angka." })
- .min(0, "jumlah harus 0 atau lebih.")
- .max(10000, "banyak amat sih ga mungkin lah bukunya segitu."),
- penerbit: z.string().nonempty("penerbit tidak boleh kosong."),
- alamat_terbit: z.string().nonempty("alamat penerbit tidak boleh kosong."),
- tahun_terbit: z
- .string()
- .regex(/^\d{0,4}$/, { message: "tahun terbit harus berupa angka 0 sampai 4 digit." }),
- penulis: z.string().nonempty("penulis tidak boleh kosong"),
-})
-
-const resolver = zodResolver(schema)
-
async function addNewBook({ valid, values }: FormSubmitEvent) {
if (!valid) return
From 8ad63952c55e6d6551108b869c5970e69afacfcf Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sat, 1 Feb 2025 05:46:04 +0000
Subject: [PATCH 5/8] fix: image upload not working
---
pages/admin/buku/tambah.vue | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/pages/admin/buku/tambah.vue b/pages/admin/buku/tambah.vue
index 80386b1..a4ad1cf 100644
--- a/pages/admin/buku/tambah.vue
+++ b/pages/admin/buku/tambah.vue
@@ -29,8 +29,7 @@ const { newImage, previewURL, previewImage } = usePreviewImage()
async function uploadBookImage(isbn: string, file: File) {
if (!formData.value) return console.trace("buku gak ada????")
- formData.value.image = `public/${isbn}`
- const { error } = await supabase.storage.from("Buku").upload(formData.value.image, file, {
+ const { error } = await supabase.storage.from("Buku").upload(`public/${isbn}`, file, {
upsert: true,
})
return error
@@ -42,10 +41,10 @@ async function addNewBook({ valid, values }: FormSubmitEvent) {
isLoading.value = true
const { data: buku, success, error } = schema.safeParse(values)
- if (!buku || !success) {
+ if (!buku || (buku && !success)) {
isLoading.value = false
- console.log(buku, success, error)
+ console.log(error)
return toast.add({
severity: "error",
@@ -55,16 +54,14 @@ async function addNewBook({ valid, values }: FormSubmitEvent) {
})
}
- const { no_isbn } = buku
-
try {
// upload image if admin puts an image
if (newImage.value) {
- const uploadError = await uploadBookImage(no_isbn, newImage.value)
+ const uploadError = await uploadBookImage(buku.no_isbn, newImage.value)
if (uploadError) throw uploadError
}
- const insertError = await insertBookData(buku)
+ const insertError = await insertBookData({ ...buku, image: `public/${buku.no_isbn}` })
if (insertError) throw insertError
dialog.value.open("Buku berhasil ditambahkan!")
From 9911ba8d0ddcd0d698ed350329c01e7e5ac85997 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sun, 2 Feb 2025 08:06:00 +0000
Subject: [PATCH 6/8] feat: add validation on buku edit form
---
pages/admin/buku/[isbn].vue | 136 ++++++++++++++++++++++++------------
1 file changed, 93 insertions(+), 43 deletions(-)
diff --git a/pages/admin/buku/[isbn].vue b/pages/admin/buku/[isbn].vue
index 2948b37..fa06416 100644
--- a/pages/admin/buku/[isbn].vue
+++ b/pages/admin/buku/[isbn].vue
@@ -9,6 +9,8 @@ import Select from "primevue/select"
import type { Database } from "~/types/database.types.ts"
import IconArrowLeft from "~icons/mdi/arrow-left"
import type { Buku } from "~/types"
+import { schema, resolver } from "~/lib/buku"
+import type { FormSubmitEvent } from "@primevue/forms"
definePageMeta({
layout: "admin",
@@ -41,21 +43,26 @@ const { data: availableCategories } = await useLazyAsyncData(
async () => await getAllAvailableCategories()
)
-async function editBook(buku: Buku) {
+async function editBook({ valid, values }: FormSubmitEvent) {
+ if (!valid) return
+
+ const { data: buku, success, error } = schema.safeParse(values)
+
+ if (!buku || !success) {
+ console.log(error)
+
+ return toast.add({
+ severity: "error",
+ summary: "gagal menyunting data buku.",
+ detail: "gagal menyunting data buku. Silahkan coba lagi dalam beberapa saat",
+ life: 10000,
+ })
+ }
+
try {
const { error } = await supabase
.from("buku")
- .update({
- judul: buku.judul,
- no_isbn: buku.no_isbn,
- penulis: buku.penulis,
- asal: buku.asal,
- kategori_id: buku.kategori_id,
- jumlah_exspl: buku.jumlah_exspl,
- penerbit: buku.penerbit,
- alamat_terbit: buku.alamat_terbit,
- tahun_terbit: buku.tahun_terbit,
- })
+ .update({ ...buku, image: `public/${buku.no_isbn}` })
.eq("no_isbn", isbn)
if (error) throw error
@@ -85,7 +92,7 @@ async function deleteBook(isbn: string) {
const { error } = await supabase.from("buku").delete().eq("no_isbn", isbn)
if (error) throw error
- const response = await supabase.storage.from("Buku").remove([`${isbn}/${isbn}`])
+ const response = await supabase.storage.from("Buku").remove([`public/${isbn}`])
if (response.error) throw response.error
if (error) throw error
@@ -153,9 +160,15 @@ const imgURL = ref(getBukuImage(buku.value?.image))
-
+
+
From 11aea0f990567c0323d97577e17ba2389584d560 Mon Sep 17 00:00:00 2001
From: Efraim Munthe
Date: Sun, 2 Feb 2025 08:18:12 +0000
Subject: [PATCH 7/8] refactor: extract upload image into separate file
---
lib/buku.ts | 11 +++++++++++
pages/admin/buku/[isbn].vue | 29 +++++++++++++++++++++++++----
pages/admin/buku/tambah.vue | 17 ++---------------
3 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/lib/buku.ts b/lib/buku.ts
index 59f9459..c481f03 100644
--- a/lib/buku.ts
+++ b/lib/buku.ts
@@ -1,3 +1,5 @@
+import { useSupabaseClient } from "#build/imports"
+import type { Database } from "~/types/database.types"
import { zodResolver } from "@primevue/forms/resolvers/zod"
import { z } from "zod"
@@ -20,3 +22,12 @@ export const schema = z.object({
})
export const resolver = zodResolver(schema)
+
+export async function uploadBookImage(isbn: string, file: File) {
+ const supabase = useSupabaseClient()
+
+ const { error } = await supabase.storage.from("Buku").upload(`public/${isbn}`, file, {
+ upsert: true,
+ })
+ return error
+}
diff --git a/pages/admin/buku/[isbn].vue b/pages/admin/buku/[isbn].vue
index fa06416..d887674 100644
--- a/pages/admin/buku/[isbn].vue
+++ b/pages/admin/buku/[isbn].vue
@@ -9,7 +9,7 @@ import Select from "primevue/select"
import type { Database } from "~/types/database.types.ts"
import IconArrowLeft from "~icons/mdi/arrow-left"
import type { Buku } from "~/types"
-import { schema, resolver } from "~/lib/buku"
+import { schema, resolver, uploadBookImage } from "~/lib/buku"
import type { FormSubmitEvent } from "@primevue/forms"
definePageMeta({
@@ -43,6 +43,9 @@ const { data: availableCategories } = await useLazyAsyncData(
async () => await getAllAvailableCategories()
)
+const { newImage, previewURL, previewImage } = usePreviewImage()
+const imgURL = ref(getBukuImage(buku.value?.image))
+
async function editBook({ valid, values }: FormSubmitEvent) {
if (!valid) return
@@ -60,6 +63,11 @@ async function editBook({ valid, values }: FormSubmitEvent) {
}
try {
+ if (newImage.value) {
+ const uploadError = await uploadBookImage(buku.no_isbn, newImage.value)
+ if (uploadError) throw uploadError
+ }
+
const { error } = await supabase
.from("buku")
.update({ ...buku, image: `public/${buku.no_isbn}` })
@@ -73,6 +81,22 @@ async function editBook({ valid, values }: FormSubmitEvent) {
})
} catch (err) {
console.trace((err as PostgrestError).message)
+
+ if (err instanceof StorageError) {
+ return toast.add({
+ severity: "error",
+ summary: "gagal mengunggah gambar buku!",
+ detail: "gagal mengunggah gambar buku, Silahkan coba lagi.",
+ life: 10000,
+ })
+ }
+
+ return toast.add({
+ severity: "error",
+ summary: "gagal menyunting data buku!",
+ detail: "gagal menyunting data buku. Silahkan coba lagi dalam beberapa saat.",
+ life: 10000,
+ })
}
}
@@ -123,9 +147,6 @@ async function deleteBook(isbn: string) {
isLoading.value = false
}
}
-
-const { newImage, previewURL, previewImage } = usePreviewImage()
-const imgURL = ref(getBukuImage(buku.value?.image))
diff --git a/pages/admin/buku/tambah.vue b/pages/admin/buku/tambah.vue
index a4ad1cf..c2dd5c4 100644
--- a/pages/admin/buku/tambah.vue
+++ b/pages/admin/buku/tambah.vue
@@ -1,13 +1,12 @@