From a5026cc28592fbc69e6ab41be92856243c166744 Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Fri, 6 Mar 2026 16:39:55 +0800 Subject: [PATCH] add: Admin Event detail screen dan komponen pendukung Deskripsi: Menambahkan halaman detail event pada admin panel dengan status parameter Menambahkan beberapa komponen UI untuk menampilkan detail event, drawer informasi, dan QR Code Update konfigurasi aplikasi dan iOS project Perbaikan pada halaman verifikasi authentication Update dokumentasi prompt untuk Qwen File yang diubah: Modified app.config.js app/(application)/admin/event/[id]/[status]/index.tsx docs/prompt-for-qwen-code.md ios/HIPMIBadungConnect.xcodeproj/project.pbxproj ios/HIPMIBadungConnect/Info.plist screens/Authentication/VerificationView.tsx New Admin Event Components screens/Admin/Event/BoxEventDetail.tsx screens/Admin/Event/EventDetailDrawer.tsx screens/Admin/Event/EventDetailQRCode.tsx screens/Admin/Event/ScreenEventDetail.tsx ### No Issue --- app.config.js | 2 +- .../admin/event/[id]/[status]/index.tsx | 253 +----------------- docs/prompt-for-qwen-code.md | 8 +- .../project.pbxproj | 54 ++++ ios/HIPMIBadungConnect/Info.plist | 2 +- screens/Admin/Event/BoxEventDetail.tsx | 85 ++++++ screens/Admin/Event/EventDetailDrawer.tsx | 37 +++ screens/Admin/Event/EventDetailQRCode.tsx | 26 ++ screens/Admin/Event/ScreenEventDetail.tsx | 163 +++++++++++ screens/Authentication/VerificationView.tsx | 3 - 10 files changed, 373 insertions(+), 260 deletions(-) create mode 100644 screens/Admin/Event/BoxEventDetail.tsx create mode 100644 screens/Admin/Event/EventDetailDrawer.tsx create mode 100644 screens/Admin/Event/EventDetailQRCode.tsx create mode 100644 screens/Admin/Event/ScreenEventDetail.tsx diff --git a/app.config.js b/app.config.js index 6dedc3d..f3ca81a 100644 --- a/app.config.js +++ b/app.config.js @@ -21,7 +21,7 @@ export default { "Aplikasi membutuhkan akses lokasi untuk menampilkan peta.", }, associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"], - buildNumber: "2", + buildNumber: "3", }, android: { diff --git a/app/(application)/admin/event/[id]/[status]/index.tsx b/app/(application)/admin/event/[id]/[status]/index.tsx index bb5bbb3..60422a0 100644 --- a/app/(application)/admin/event/[id]/[status]/index.tsx +++ b/app/(application)/admin/event/[id]/[status]/index.tsx @@ -1,254 +1,5 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { - ActionIcon, - AlertDefaultSystem, - BadgeCustom, - BaseBox, - DrawerCustom, - LoaderCustom, - MenuDrawerDynamicGrid, - Spacing, - StackCustom, - TextCustom, - ViewWrapper, -} from "@/components"; -import { IconDot, IconList } from "@/components/_Icon/IconComponent"; -import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; -import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; -import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; -import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; -import ReportBox from "@/components/Box/ReportBox"; -import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; -import { useAuth } from "@/hooks/use-auth"; -import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus"; -import { apiAdminEventById } from "@/service/api-admin/api-admin-event"; -import { DEEP_LINK_URL } from "@/service/api-config"; -import { colorBadgeStatus } from "@/utils/colorBadge"; -import { dateTimeView } from "@/utils/dateTimeView"; -import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; -import _ from "lodash"; -import React, { useCallback } from "react"; -import QRCode from "react-native-qrcode-svg"; -import Toast from "react-native-toast-message"; +import { Admin_ScreenEventDetail } from "@/screens/Admin/Event/ScreenEventDetail"; export default function AdminEventDetail() { - const { user } = useAuth(); - const { id, status } = useLocalSearchParams(); - const [openDrawer, setOpenDrawer] = React.useState(false); - - const [data, setData] = React.useState(null); - const [loadData, setLoadData] = React.useState(false); - const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`; - const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`; - - const isDevLink = - process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL; - - useFocusEffect( - useCallback(() => { - onLoadData(); - }, [id]) - ); - const onLoadData = async () => { - try { - setLoadData(true); - const response = await apiAdminEventById({ - id: id as string, - }); - - if (response.success) { - setData(response.data); - } - } catch (error) { - console.log("[ERROR]", error); - } finally { - setLoadData(false); - } - }; - - const listData = [ - { - label: "Pembuat Event", - value: (data && data?.Author?.username) || "-", - }, - { - label: "Judul Event", - value: (data && data?.title) || "-", - }, - { - label: "Status", - value: - (data && ( - - {_.startCase(status as string)} - - )) || - "-", - }, - { - label: "Lokasi", - value: (data && data?.lokasi) || "-", - }, - { - label: "Tipe Acara", - value: (data && data?.EventMaster_TipeAcara?.name) || "-", - }, - { - label: "Mulai Event", - value: - (data && data?.tanggal && dateTimeView({ date: data?.tanggal })) || "-", - }, - { - label: "Event Berakhir", - value: - (data && - data?.tanggalSelesai && - dateTimeView({ date: data?.tanggalSelesai })) || - "-", - }, - { - label: "Deskripsi", - value: (data && data?.deskripsi) || "-", - }, - ]; - - const rightComponent = ( - } - onPress={() => { - setOpenDrawer(true); - }} - /> - ); - - const handlerSubmit = async () => { - try { - const response = await funUpdateStatusEvent({ - id: id as string, - changeStatus: "publish", - data: { catatan: "", senderId: user?.id as string }, - }); - - if (!response.success) { - Toast.show({ - type: "error", - text1: "Gagal mempublikasikan event", - }); - return; - } - - Toast.show({ - type: "success", - text1: "Event berhasil dipublikasikan", - }); - router.back(); - } catch (error) { - console.log("[ERROR]", error); - } - }; - - return ( - <> - - } - > - - - {listData.map((item, i) => ( - {item.label}} - value={{item.value}} - /> - ))} - - - - - - {data && - data?.catatan && - (status === "reject" || status === "review") && ( - - )} - - {(status === "publish" || status === "history") && ( - - - QR Code Event - {loadData ? ( - - ) : ( - - )} - - {/* {isDevLink} */} - - - )} - - {status === "review" && ( - { - AlertDefaultSystem({ - title: "Publish", - message: "Apakah anda yakin ingin mempublikasikan data ini?", - textLeft: "Batal", - textRight: "Ya", - onPressRight: () => handlerSubmit(), - }); - }} - onReject={() => { - router.push(`/admin/event/${id}/reject-input?status=${status}`); - }} - /> - )} - - {status === "reject" && ( - { - router.push(`/admin/event/${id}/reject-input?status=${status}`); - }} - /> - )} - - - - setOpenDrawer(false)} - height={"auto"} - > - , - path: `/admin/event/${id}/list-of-participants`, - }, - ]} - onPressItem={(item) => { - setOpenDrawer(false); - router.push(item.path as any); - }} - /> - - - ); + return ; } diff --git a/docs/prompt-for-qwen-code.md b/docs/prompt-for-qwen-code.md index a7682cd..d81d1e3 100644 --- a/docs/prompt-for-qwen-code.md +++ b/docs/prompt-for-qwen-code.md @@ -55,10 +55,10 @@ Component yang digunakan: components/_ShareComponent/NewWrapper.tsx -File source: app/(application)/admin/forum/[id]/list-comment.tsx -Folder tujuan: screens/Admin/Forum -Nama file utama: ScreenForumListComment.tsx -Nama function utama: Admin_ScreenForumListComment +File source: app/(application)/admin/event/[id]/[status]/index.tsx +Folder tujuan: screens/Admin/Event +Nama file utama: ScreenEventDetail.tsx +Nama function utama: Admin_ScreenEventDetail File komponen wrapper: components/_ShareComponent/NewWrapper.tsx Buat file baru pada "Folder tujuan" dengan nama "Nama file utama" dan ubah nama function menjadi "Nama function utama" kemudian clean code, import dan panggil function tersebut pada file "File source" diff --git a/ios/HIPMIBadungConnect.xcodeproj/project.pbxproj b/ios/HIPMIBadungConnect.xcodeproj/project.pbxproj index 060c306..d7d08be 100644 --- a/ios/HIPMIBadungConnect.xcodeproj/project.pbxproj +++ b/ios/HIPMIBadungConnect.xcodeproj/project.pbxproj @@ -180,6 +180,9 @@ 469F2CAA8928481CA86EB0F4 /* Remove signature files (Xcode workaround) */, 0F9297956F4F4FC9881920F8 /* Remove signature files (Xcode workaround) */, 058D2457CFA64FD9AC31C74F /* Remove signature files (Xcode workaround) */, + FB0CB57BF4D74C1D87C2036C /* Remove signature files (Xcode workaround) */, + 14B3DE54EE4049AEB1EADA6B /* Remove signature files (Xcode workaround) */, + B4CF5E09DBB44A4FB9CB91B9 /* Remove signature files (Xcode workaround) */, ); buildRules = ( ); @@ -941,6 +944,57 @@ rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\"; "; }; + FB0CB57BF4D74C1D87C2036C /* Remove signature files (Xcode workaround) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Remove signature files (Xcode workaround)"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " + echo \"Remove signature files (Xcode workaround)\"; + rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\"; + "; + }; + 14B3DE54EE4049AEB1EADA6B /* Remove signature files (Xcode workaround) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Remove signature files (Xcode workaround)"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " + echo \"Remove signature files (Xcode workaround)\"; + rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\"; + "; + }; + B4CF5E09DBB44A4FB9CB91B9 /* Remove signature files (Xcode workaround) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + name = "Remove signature files (Xcode workaround)"; + inputPaths = ( + ); + outputPaths = ( + ); + shellPath = /bin/sh; + shellScript = " + echo \"Remove signature files (Xcode workaround)\"; + rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\"; + "; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/ios/HIPMIBadungConnect/Info.plist b/ios/HIPMIBadungConnect/Info.plist index cc5cc95..f55aadf 100644 --- a/ios/HIPMIBadungConnect/Info.plist +++ b/ios/HIPMIBadungConnect/Info.plist @@ -39,7 +39,7 @@ CFBundleVersion - 2 + 3 ITSAppUsesNonExemptEncryption LSMinimumSystemVersion diff --git a/screens/Admin/Event/BoxEventDetail.tsx b/screens/Admin/Event/BoxEventDetail.tsx new file mode 100644 index 0000000..f69d050 --- /dev/null +++ b/screens/Admin/Event/BoxEventDetail.tsx @@ -0,0 +1,85 @@ +import { BadgeCustom, BaseBox, Spacing, StackCustom, TextCustom } from "@/components"; +import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; +import { colorBadgeStatus } from "@/utils/colorBadge"; +import { dateTimeView } from "@/utils/dateTimeView"; +import _ from "lodash"; + +interface EventDetailData { + Author?: { + username?: string; + }; + title?: string; + lokasi?: string; + EventMaster_TipeAcara?: { + name?: string; + }; + tanggal?: string; + tanggalSelesai?: string; + deskripsi?: string; + catatan?: string; +} + +interface BoxEventDetailProps { + data: EventDetailData | null; + status: string; +} + +export function BoxEventDetail({ data, status }: BoxEventDetailProps) { + const listData = [ + { + label: "Pembuat Event", + value: data?.Author?.username || "-", + }, + { + label: "Judul Event", + value: data?.title || "-", + }, + { + label: "Status", + value: data ? ( + + {_.startCase(status)} + + ) : ( + "-" + ), + }, + { + label: "Lokasi", + value: data?.lokasi || "-", + }, + { + label: "Tipe Acara", + value: data?.EventMaster_TipeAcara?.name || "-", + }, + { + label: "Mulai Event", + value: data?.tanggal ? dateTimeView({ date: data.tanggal }) : "-", + }, + { + label: "Event Berakhir", + value: data?.tanggalSelesai + ? dateTimeView({ date: data.tanggalSelesai }) + : "-", + }, + { + label: "Deskripsi", + value: data?.deskripsi || "-", + }, + ]; + + return ( + + + {listData.map((item, i) => ( + {item.label}} + value={{item.value}} + /> + ))} + + + + ); +} diff --git a/screens/Admin/Event/EventDetailDrawer.tsx b/screens/Admin/Event/EventDetailDrawer.tsx new file mode 100644 index 0000000..8a7ebaa --- /dev/null +++ b/screens/Admin/Event/EventDetailDrawer.tsx @@ -0,0 +1,37 @@ +import { DrawerCustom, MenuDrawerDynamicGrid } from "@/components"; +import { IconList } from "@/components/_Icon/IconComponent"; +import { router } from "expo-router"; + +interface EventDetailDrawerProps { + isVisible: boolean; + onClose: () => void; + eventId: string; +} + +export function EventDetailDrawer({ + isVisible, + onClose, + eventId, +}: EventDetailDrawerProps) { + return ( + + , + path: `/admin/event/${eventId}/list-of-participants`, + }, + ]} + onPressItem={(item) => { + onClose(); + router.push(item.path as any); + }} + /> + + ); +} diff --git a/screens/Admin/Event/EventDetailQRCode.tsx b/screens/Admin/Event/EventDetailQRCode.tsx new file mode 100644 index 0000000..7604aa6 --- /dev/null +++ b/screens/Admin/Event/EventDetailQRCode.tsx @@ -0,0 +1,26 @@ +import { BaseBox, LoaderCustom, Spacing, StackCustom, TextCustom } from "@/components"; +import QRCode from "react-native-qrcode-svg"; + +interface EventDetailQRCodeProps { + qrValue: string; + isLoading: boolean; +} + +export function EventDetailQRCode({ qrValue, isLoading }: EventDetailQRCodeProps) { + return ( + + + QR Code Event + {isLoading ? ( + + ) : ( + + )} + + + + ); +} diff --git a/screens/Admin/Event/ScreenEventDetail.tsx b/screens/Admin/Event/ScreenEventDetail.tsx new file mode 100644 index 0000000..195676d --- /dev/null +++ b/screens/Admin/Event/ScreenEventDetail.tsx @@ -0,0 +1,163 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { ActionIcon, AlertDefaultSystem } from "@/components"; +import { IconDot } from "@/components/_Icon/IconComponent"; +import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; +import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; +import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; +import ReportBox from "@/components/Box/ReportBox"; +import NewWrapper from "@/components/_ShareComponent/NewWrapper"; +import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; +import { useAuth } from "@/hooks/use-auth"; +import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus"; +import { apiAdminEventById } from "@/service/api-admin/api-admin-event"; +import { DEEP_LINK_URL } from "@/service/api-config"; +import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; +import { useCallback, useMemo, useState } from "react"; +import Toast from "react-native-toast-message"; +import { BoxEventDetail } from "./BoxEventDetail"; +import { EventDetailDrawer } from "./EventDetailDrawer"; +import { EventDetailQRCode } from "./EventDetailQRCode"; + +export function Admin_ScreenEventDetail() { + const { user } = useAuth(); + const { id, status } = useLocalSearchParams(); + const [openDrawer, setOpenDrawer] = useState(false); + const [data, setData] = useState(null); + const [loadData, setLoadData] = useState(false); + + const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`; + const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`; + const isDevLink = + process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL; + + useFocusEffect( + useCallback(() => { + onLoadData(); + }, [id]), + ); + + const onLoadData = async () => { + try { + setLoadData(true); + const response = await apiAdminEventById({ + id: id as string, + }); + + if (response.success) { + setData(response.data); + } + } catch (error) { + console.log("[ERROR]", error); + } finally { + setLoadData(false); + } + }; + + const rightComponent = ( + } + onPress={() => { + setOpenDrawer(true); + }} + /> + ); + + const handlerSubmit = async () => { + try { + const response = await funUpdateStatusEvent({ + id: id as string, + changeStatus: "publish", + data: { catatan: "", senderId: user?.id as string }, + }); + + if (!response.success) { + Toast.show({ + type: "error", + text1: "Gagal mempublikasikan event", + }); + return; + } + + Toast.show({ + type: "success", + text1: "Event berhasil dipublikasikan", + }); + router.back(); + } catch (error) { + console.log("[ERROR]", error); + } + }; + + const headerComponent = useMemo( + () => ( + + ), + [status], + ); + + const footerComponent = useMemo(() => { + if (status === "review") { + return ( + { + AlertDefaultSystem({ + title: "Publish", + message: "Apakah anda yakin ingin mempublikasikan data ini?", + textLeft: "Batal", + textRight: "Ya", + onPressRight: () => handlerSubmit(), + }); + }} + onReject={() => { + router.push(`/admin/event/${id}/reject-input?status=${status}`); + }} + /> + ); + } + + if (status === "reject") { + return ( + { + router.push(`/admin/event/${id}/reject-input?status=${status}`); + }} + /> + ); + } + + return null; + }, [status, id]); + + return ( + <> + + + + {data?.catatan && (status === "reject" || status === "review") && ( + + )} + + {(status === "publish" || status === "history") && ( + + )} + + + setOpenDrawer(false)} + eventId={id as string} + /> + + ); +} diff --git a/screens/Authentication/VerificationView.tsx b/screens/Authentication/VerificationView.tsx index 7ed8add..b2f6087 100644 --- a/screens/Authentication/VerificationView.tsx +++ b/screens/Authentication/VerificationView.tsx @@ -5,7 +5,6 @@ import { MainColor } from "@/constants/color-palet"; import { useAuth } from "@/hooks/use-auth"; import { apiCheckCodeOtp } from "@/service/api-config"; import { GStyles } from "@/styles/global-styles"; -import { registerForPushNotificationsAsync } from "@/utils/notifications"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; @@ -17,8 +16,6 @@ import Toast from "react-native-toast-message"; export default function VerificationView() { const { nomor } = useLocalSearchParams<{ nomor: string }>(); - console.log("[NOMOR]", nomor); - const [inputOtp, setInputOtp] = useState(""); const [userNumber, setUserNumber] = useState(""); const [loading, setLoading] = useState(false); -- 2.49.1