From 9733efb4f3683d10a31c9cb6d029bec631ddb9b2 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 16 May 2025 14:32:56 +0800 Subject: [PATCH] feat admin sticker deksrupsi: - edit stiker - hapu stiker --- src/app/api/sticker/[id]/route.ts | 137 +++++++ src/app/api/sticker/route.ts | 3 + .../app-information/sticker/[id]/page.tsx | 9 + .../admin/app_info/lib/api_fetch_stiker.tsx | 100 ++++++ .../admin/app_info/ui/ui_layout_admin_app.tsx | 2 +- .../view/sticker/view_detail_sticker.tsx | 337 ++++++++++++++++++ .../app_info/view/view_create_sticker.tsx | 1 - .../admin/app_info/view/view_stiker.tsx | 210 +++++++---- .../router_admin/router_app_information.ts | 1 + 9 files changed, 728 insertions(+), 72 deletions(-) create mode 100644 src/app/api/sticker/[id]/route.ts create mode 100644 src/app/dev/admin/app-information/sticker/[id]/page.tsx create mode 100644 src/app_modules/admin/app_info/view/sticker/view_detail_sticker.tsx diff --git a/src/app/api/sticker/[id]/route.ts b/src/app/api/sticker/[id]/route.ts new file mode 100644 index 00000000..a079c267 --- /dev/null +++ b/src/app/api/sticker/[id]/route.ts @@ -0,0 +1,137 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@/lib"; + +export { GET, PUT, DELETE }; + +async function GET(request: Request, { params }: { params: { id: string } }) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + const { id } = params; + + const sticker = await prisma.sticker.findUnique({ + where: { + id: id, + }, + include: { + MasterEmotions: true, + }, + }); + + return NextResponse.json( + { success: true, message: "Success get data sticker", data: sticker }, + { status: 200 } + ); + } catch (error) { + console.error("Error get data sticker", error); + return NextResponse.json( + { success: false, message: "Error get data sticker" }, + { status: 500 } + ); + } +} + +async function PUT(request: Request, { params }: { params: { id: string } }) { + const method = request.method; + if (method !== "PUT") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + const { id } = params; + const data = await request.json(); + + if (data.fileId) { + const updatedDataWithFile = await prisma.sticker.update({ + where: { + id: id, + }, + data: { + fileId: data.fileId, + MasterEmotions: { + set: data.emotions.map((value: string) => ({ value })), // ✅ replace relasi + }, + }, + }); + + return NextResponse.json({ + success: true, + message: "Sticker updated successfully", + data: updatedDataWithFile, + }); + } + + const updatedDataWithoutFile = await prisma.sticker.update({ + where: { + id: id, + }, + data: { + MasterEmotions: { + set: data.emotions.map((value: string) => ({ value })), // ✅ replace relasi + }, + }, + }); + + if (!updatedDataWithoutFile) { + return NextResponse.json( + { success: false, message: "Failed to update sticker" }, + { status: 400 } + ); + } + + return NextResponse.json({ + success: true, + message: "Sticker updated successfully", + data: updatedDataWithoutFile, + }); + } catch (error) { + console.error("Error updating sticker:", error); + return NextResponse.json( + { success: false, message: "Failed to update sticker" }, + { status: 500 } + ); + } +} + +async function DELETE( + request: Request, + { params }: { params: { id: string } } +) { + const method = request.method; + if (method !== "DELETE") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + const { id } = params; + + const sticker = await prisma.sticker.delete({ + where: { + id: id, + }, + }); + + return NextResponse.json( + { success: true, message: "Success delete sticker", data: sticker }, + { status: 200 } + ); + } catch (error) { + console.error("Error delete sticker", error); + return NextResponse.json( + { success: false, message: "Error delete sticker" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/sticker/route.ts b/src/app/api/sticker/route.ts index 655db60e..72bd6541 100644 --- a/src/app/api/sticker/route.ts +++ b/src/app/api/sticker/route.ts @@ -61,6 +61,9 @@ async function GET(request: Request) { try { const sticker = await prisma.sticker.findMany({ + orderBy: { + updatedAt: "desc", + }, include: { MasterEmotions: true, }, diff --git a/src/app/dev/admin/app-information/sticker/[id]/page.tsx b/src/app/dev/admin/app-information/sticker/[id]/page.tsx new file mode 100644 index 00000000..288ef4f6 --- /dev/null +++ b/src/app/dev/admin/app-information/sticker/[id]/page.tsx @@ -0,0 +1,9 @@ +import AdminAppInformation_ViewStickerDetail from "@/app_modules/admin/app_info/view/sticker/view_detail_sticker"; + +export default function Page() { + return ( + <> + + + ); +} diff --git a/src/app_modules/admin/app_info/lib/api_fetch_stiker.tsx b/src/app_modules/admin/app_info/lib/api_fetch_stiker.tsx index 48f048c0..6e26cc30 100644 --- a/src/app_modules/admin/app_info/lib/api_fetch_stiker.tsx +++ b/src/app_modules/admin/app_info/lib/api_fetch_stiker.tsx @@ -65,3 +65,103 @@ export const apiAdminGetSticker = async () => { } }; +export const apiAdminGetStickerById = async ({ id }: { id: string }) => { + try { + // Fetch token from cookie + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) { + console.error("No token found"); + return null; + } + + const response = await fetch(`/api/sticker/${id}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + // Check if the response is OK + if (!response.ok) { + const errorData = await response.json().catch(() => null); + console.error("Failed to get sticker", response.statusText, errorData); + throw new Error(errorData?.message || "Failed to get sticker"); + } + + // Return the JSON response + return await response.json(); + } catch (error) { + console.error("Error get sticker", error); + throw error; // Re-throw the error to handle it in the calling function + } +}; + + +export const apiAdminUpdateSticker = async ({ data }: { data: any }) => { + try { + // Fetch token from cookie + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) { + console.error("No token found"); + return null; + } + + const response = await fetch(`/api/sticker/${data.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(data), + }); + + // Check if the response is OK + if (!response.ok) { + const errorData = await response.json().catch(() => null); + console.error("Failed to update sticker", response.statusText, errorData); + throw new Error(errorData?.message || "Failed to update sticker"); + } + + // Return the JSON response + return await response.json(); + } catch (error) { + console.error("Error update sticker", error); + throw error; // Re-throw the error to handle it in the calling function + } +}; + +export const apiAdminDeleteSticker = async ({ id }: { id: string }) => { + try { + // Fetch token from cookie + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) { + console.error("No token found"); + return null; + } + + const response = await fetch(`/api/sticker/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + // Check if the response is OK + if (!response.ok) { + const errorData = await response.json().catch(() => null); + console.error("Failed to delete sticker", response.statusText, errorData); + throw new Error(errorData?.message || "Failed to delete sticker"); + } + + // Return the JSON response + return await response.json(); + } catch (error) { + console.error("Error delete sticker", error); + throw error; // Re-throw the error to handle it in the calling function + } +}; \ No newline at end of file diff --git a/src/app_modules/admin/app_info/ui/ui_layout_admin_app.tsx b/src/app_modules/admin/app_info/ui/ui_layout_admin_app.tsx index 44fb9df2..2a268168 100644 --- a/src/app_modules/admin/app_info/ui/ui_layout_admin_app.tsx +++ b/src/app_modules/admin/app_info/ui/ui_layout_admin_app.tsx @@ -55,7 +55,7 @@ export default function AdminAppInformation_Layout({ const handleClick = async (path: string) => { if (path === pathname) return; // kalau sudah di halaman itu, jangan reload - setLoadingPath(path); + // setLoadingPath(path); router.push(path); }; diff --git a/src/app_modules/admin/app_info/view/sticker/view_detail_sticker.tsx b/src/app_modules/admin/app_info/view/sticker/view_detail_sticker.tsx new file mode 100644 index 00000000..5ee892b4 --- /dev/null +++ b/src/app_modules/admin/app_info/view/sticker/view_detail_sticker.tsx @@ -0,0 +1,337 @@ +"use client"; + +import { MainColor } from "@/app_modules/_global/color"; +import { + ComponentGlobal_BoxUploadImage, + ComponentGlobal_ButtonUploadFileImage, +} from "@/app_modules/_global/component"; +import Component_V3_Label_TextInput from "@/app_modules/_global/component/new/comp_V3_label_text_input"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; +import { apiGetMasterEmotions } from "@/app_modules/_global/lib/api_fetch_master"; +import { ISticker } from "@/app_modules/_global/lib/interface/stiker"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { ComponentAdminGlobal_TitlePage } from "@/app_modules/admin/_admin_global/_component"; +import { Admin_ComponentBoxStyle } from "@/app_modules/admin/_admin_global/_component/comp_admin_boxstyle"; +import { Admin_ComponentModal } from "@/app_modules/admin/_admin_global/_component/comp_admin_modal"; +import { ComponentAdminGlobal_NotifikasiBerhasil } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_berhasil"; +import { ComponentAdminGlobal_NotifikasiGagal } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_gagal"; +import Admin_ComponentBackButton from "@/app_modules/admin/_admin_global/back_button"; +import { Admin_V3_ComponentBreakpoint } from "@/app_modules/admin/_components_v3/comp_simple_grid_breakpoint"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import { APIs, DIRECTORY_ID, pathAssetImage } from "@/lib"; +import { + AspectRatio, + Box, + Button, + Center, + Chip, + Group, + Image, + Stack, + Text, + Title, +} from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconCheck, IconTrash } from "@tabler/icons-react"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { + apiAdminDeleteSticker, + apiAdminGetStickerById, + apiAdminUpdateSticker, +} from "../../lib/api_fetch_stiker"; +import { AdminColor } from "@/app_modules/_global/color/color_pallet"; + +export default function AdminAppInformation_ViewStickerDetail() { + const router = useRouter(); + const param = useParams<{ id: string }>(); + const [file, setFile] = useState(null); + const [img, setImg] = useState(null); + const [listEmotion, setListEmotion] = useState([]); + const [valueEmotion, setValueEmotion] = useState([]); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [loadingDelete, setLoadingDelete] = useState(false); + const [openModalDelete, setOpenModalDelete] = useState(false); + + useShallowEffect(() => { + onLoadData(); + onLoadMasterEmotions(); + }, []); + + async function onLoadData() { + try { + const response = await apiAdminGetStickerById({ id: param.id }); + if (response.success) { + setData(response.data); + setValueEmotion(response.data.MasterEmotions.map((e: any) => e.value)); + } + } catch (error) { + console.error("Error fetching data", error); + } + } + + async function onLoadMasterEmotions() { + try { + const response = await apiGetMasterEmotions(); + + if (response.success) { + setListEmotion(response.data); + } + } catch (error) { + console.error("Error on load master emotions:", error); + } + } + + async function onUploadFile() { + try { + const response = await funGlobal_UploadToStorage({ + file: file as File, + dirId: DIRECTORY_ID.sticker, + }); + + if (!response.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar"); + return; + } else { + const deleteFile = await funGlobal_DeleteFileById({ + fileId: data?.fileId as string, + }); + + if (!deleteFile.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal delete gambar"); + return; + } + + return response.data.id; + } + } catch (error) { + console.error("Error on upload file", error); + } + } + + async function handleUpdateSticker({ fileId }: { fileId?: string }) { + try { + const response = await apiAdminUpdateSticker({ + data: { + emotions: valueEmotion, + fileId: fileId || "", + id: param.id, + }, + }); + + if (response.success) { + ComponentAdminGlobal_NotifikasiBerhasil("Berhasil disimpan"); + router.back(); + } else { + setLoading(false); + throw new Error("Failed to create sticker"); + } + } catch (error) { + setLoading(false); + ComponentAdminGlobal_NotifikasiGagal("Gagal disimpan"); + console.error("Error create sticker", error); + } + } + + async function onSubmit() { + try { + setLoading(true); + + if (file) { + const uploadFile = await onUploadFile(); + + if (!uploadFile) { + setLoading(false); + return; + } + + await handleUpdateSticker({ fileId: uploadFile }); + } else { + await handleUpdateSticker({}); + } + } catch (error) { + console.error("Error on create sticker", error); + } + } + + async function onDelete() { + try { + setLoadingDelete(true); + const response = await apiAdminDeleteSticker({ id: param.id }); + + if (response.success) { + const deleteFile = await funGlobal_DeleteFileById({ + fileId: data?.fileId as string, + }); + + if (!deleteFile.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal delete gambar"); + return; + } + + setLoadingDelete(false); + ComponentAdminGlobal_NotifikasiBerhasil("Berhasil dihapus"); + router.back(); + } else { + setLoadingDelete(false); + ComponentGlobal_NotifikasiPeringatan("Gagal dihapus"); + } + } catch (error) { + setLoadingDelete(false); + ComponentAdminGlobal_NotifikasiGagal("Proses hapus error"); + console.error("Error delete sticker", error); + } + } + + return ( + <> + + + + {/*
+          {JSON.stringify(valueEmotion, null, 2)}
+        
*/} + + + {!listEmotion.length || !data ? ( + + ) : ( + + + + + {img ? ( + + Foto + + ) : ( + + Foto + + )} + + +
+ +
+
+ + + + + + + {listEmotion.map((e, i) => { + return ( + + {e.label} + + ); + })} + + + + + + + + + + +
+
+ )} +
+ + setOpenModalDelete(false)} + withCloseButton={false} + closeOnClickOutside={false} + size="md" + > + + + Apakah anda yakin ingin menghapus stiker ini? + + + + + + + +
+ + ); +} diff --git a/src/app_modules/admin/app_info/view/view_create_sticker.tsx b/src/app_modules/admin/app_info/view/view_create_sticker.tsx index 648e1990..af8c828d 100644 --- a/src/app_modules/admin/app_info/view/view_create_sticker.tsx +++ b/src/app_modules/admin/app_info/view/view_create_sticker.tsx @@ -261,7 +261,6 @@ export default function AdminAppInformation_ViewCreateSticker() { - - - -
- - Sticker - -
- - -
- - - - {e.MasterEmotions.map((e) => ( - {e.value} - ))} - - - -
- - - )); - }; - return ( <> + + + {/* +
+ { + setIsActivation(true); + setUpdateStatus({ + id: e?.id, + active: val.currentTarget.checked as any, + }); + }} + /> +
+ */} + +
+ + Sticker + +
+ + + + + + {e.MasterEmotions.map((e) => ( + {e.value} + ))} + + + + + + )); +}; diff --git a/src/lib/router_admin/router_app_information.ts b/src/lib/router_admin/router_app_information.ts index 42308556..9218a74f 100644 --- a/src/lib/router_admin/router_app_information.ts +++ b/src/lib/router_admin/router_app_information.ts @@ -6,4 +6,5 @@ export const RouterAdminAppInformation = { // Sticker sticker: "/dev/admin/app-information/sticker", createSticker: "/dev/admin/app-information/sticker/create", + detailSticker: ({ id }: { id: string }) => `/dev/admin/app-information/sticker/${id}`, };